diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 382d174b1..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CI - -on: - push: - branches: - - main - paths-ignore: - - '*.md' - pull_request: - paths-ignore: - - '*.md' - -env: - CARGO_MAKE_TOOLCHAIN: nightly-2023-12-20 - -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 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8aa0186de..000000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target/ -bin/midenc -bin/filecheck diff --git a/bin/.gitkeep b/.nojekyll similarity index 100% rename from bin/.gitkeep rename to .nojekyll diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index a9290490d..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,5727 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "opaque-debug", -] - -[[package]] -name = "ahash" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" -dependencies = [ - "cfg-if", - "const-random", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "aligned" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80a21b9440a626c7fc8573a9e3d3a06b75c7c97754c2949bc7857b90353ca655" -dependencies = [ - "as-slice", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anstream" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" - -[[package]] -name = "anstyle-parse" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - -[[package]] -name = "anyhow" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca87830a3e3fb156dc96cfbd31cb620265dd053be734723f22b760d6cc3c3051" - -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - -[[package]] -name = "async-broadcast" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" -dependencies = [ - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" -dependencies = [ - "concurrent-queue", - "event-listener 4.0.2", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" -dependencies = [ - "async-lock 3.2.0", - "async-task", - "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.1.0", - "slab", -] - -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "blocking", - "futures-lite 1.13.0", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" -dependencies = [ - "async-lock 3.2.0", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.1.0", - "parking", - "polling 3.3.1", - "rustix 0.38.28", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" -dependencies = [ - "event-listener 4.0.2", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-process" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" -dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", - "async-signal", - "blocking", - "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.28", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-recursion" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "async-signal" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" -dependencies = [ - "async-io 2.2.2", - "async-lock 2.8.0", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 0.38.28", - "signal-hook-registry", - "slab", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-task" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d90cd0b264dfdd8eb5bad0a2c217c1f88fa96a8573f40e7b12de23fb468f46" - -[[package]] -name = "async-trait" -version = "0.1.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auth-git2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e7771d4ab6635cbd685ce8db215b29c78a468098126de77c57f3b2e6eb3757" -dependencies = [ - "dirs", - "git2", - "terminal-prompt", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "blake3" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-modes" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" -dependencies = [ - "block-padding", - "cipher", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" -dependencies = [ - "async-channel", - "async-lock 3.2.0", - "async-task", - "fastrand 2.0.1", - "futures-io", - "futures-lite 2.1.0", - "piper", - "tracing", -] - -[[package]] -name = "bstr" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "btoi" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" -dependencies = [ - "num-traits", -] - -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" - -[[package]] -name = "camino" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-component" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8abe1e8a19a9d5c26ff5bb99bf9325f161d6e0ba196fe6cebbd8a50260ca346" -dependencies = [ - "anyhow", - "bytes", - "cargo-component-core", - "cargo_metadata", - "clap 4.4.12", - "futures", - "heck", - "indexmap 2.1.0", - "libc", - "log", - "p256", - "parse_arg", - "pretty_env_logger", - "rand_core", - "rpassword", - "semver", - "serde", - "serde_json", - "tokio", - "tokio-util", - "toml_edit 0.21.0", - "url", - "warg-client", - "warg-crypto", - "warg-protocol", - "wasm-metadata", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-component", - "wit-parser", -] - -[[package]] -name = "cargo-component-core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93484e75d9d9bb99d53bdd56873501f5ddb3be72f6b15db6359b9372646757f1" -dependencies = [ - "anyhow", - "clap 4.4.12", - "futures", - "indexmap 2.1.0", - "keyring", - "libc", - "log", - "owo-colors", - "semver", - "serde", - "tokio", - "toml_edit 0.21.0", - "unicode-width", - "url", - "warg-client", - "warg-crypto", - "warg-protocol", - "windows-sys 0.52.0", - "wit-component", - "wit-parser", -] - -[[package]] -name = "cargo-generate" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2885ae054e000b117515ab33e91c10eca90c2788a7baec1b97ada1f1f51e57" -dependencies = [ - "anyhow", - "auth-git2", - "clap 4.4.12", - "console", - "dialoguer", - "env_logger 0.10.1", - "fs-err", - "git2", - "gix-config", - "heck", - "home", - "ignore", - "indexmap 2.1.0", - "indicatif", - "liquid", - "liquid-core", - "liquid-derive", - "liquid-lib", - "log", - "names", - "paste", - "path-absolutize", - "regex", - "remove_dir_all", - "rhai", - "sanitize-filename", - "semver", - "serde", - "tempfile", - "thiserror", - "toml 0.8.8", - "walkdir", -] - -[[package]] -name = "cargo-miden" -version = "0.1.0" -dependencies = [ - "anyhow", - "cargo-component", - "cargo-component-core", - "cargo-generate", - "cargo_metadata", - "clap 4.4.12", - "env_logger 0.9.3", - "log", - "miden-diagnostics", - "midenc-compile", - "midenc-session", - "parse_arg", - "path-absolutize", - "semver", -] - -[[package]] -name = "cargo-platform" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "serde", - "windows-targets 0.48.5", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "4.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim 0.10.0", -] - -[[package]] -name = "clap_derive" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "clap_lex" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - -[[package]] -name = "codespan" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e" -dependencies = [ - "codespan-reporting", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "concat-idents" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d" -dependencies = [ - "quote", - "syn 2.0.45", -] - -[[package]] -name = "concurrent-queue" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.45.0", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const-random" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "cpufeatures" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" -dependencies = [ - "libc", -] - -[[package]] -name = "cranelift-bforest" -version = "0.100.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751cbf89e513f283c0641eb7f95dc72fda5051dd95ca203d1dc45e26bc89dba8" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-entity" -version = "0.100.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc619b86fe3c72f43fc417c9fd67a04ec0c98296e5940922d9fd9e6eedf72521" - -[[package]] -name = "crossbeam-deque" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "cvt" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 2.0.45", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "der" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "dialoguer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" -dependencies = [ - "console", - "shell-words", - "tempfile", - "thiserror", - "zeroize", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dissimilar" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "pem-rfc7468", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enumflags2" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "env_logger" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "backtrace", - "version_check", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "218a870470cce1469024e9fb66b901aa983929d81304a1cdb299f28118e550d5" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.2", - "pin-project-lite", -] - -[[package]] -name = "expect-test" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" -dependencies = [ - "dissimilar", - "once_cell", -] - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "faster-hex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" -dependencies = [ - "serde", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fastrand" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "filecheck" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 4.4.12", - "lit", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flurry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0afc943ef18eebf6bc3335daeb8d338202093d18444a1784ea7f57fe7680f8" -dependencies = [ - "ahash 0.7.7", - "num_cpus", - "parking_lot", - "seize", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs-err" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" -dependencies = [ - "autocfg", -] - -[[package]] -name = "fs_at" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982f82cc75107eef84f417ad6c53ae89bf65b561937ca4a3b3b0fd04d0aa2425" -dependencies = [ - "aligned", - "cfg-if", - "cvt", - "libc", - "nix", - "windows-sys 0.48.0", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" -dependencies = [ - "fastrand 2.0.1", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -dependencies = [ - "fallible-iterator", - "stable_deref_trait", -] - -[[package]] -name = "git2" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd" -dependencies = [ - "bitflags 2.4.1", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] - -[[package]] -name = "gix-actor" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadca029ef716b4378f7afb19f7ee101fde9e58ba1f1445971315ac866db417" -dependencies = [ - "bstr", - "btoi", - "gix-date", - "itoa", - "thiserror", - "winnow", -] - -[[package]] -name = "gix-config" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cae98c6b4c66c09379bc35274b172587d6b0ac369a416c39128ad8c6454f9bb" -dependencies = [ - "bstr", - "gix-config-value", - "gix-features", - "gix-glob", - "gix-path", - "gix-ref", - "gix-sec", - "memchr", - "once_cell", - "smallvec", - "thiserror", - "unicode-bom", - "winnow", -] - -[[package]] -name = "gix-config-value" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0be46f4cf1f8f9e88d0e3eb7b29718aff23889563249f379119bd1ab6910e" -dependencies = [ - "bitflags 2.4.1", - "bstr", - "gix-path", - "libc", - "thiserror", -] - -[[package]] -name = "gix-date" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7f3dfb72bebe3449b5e642be64e3c6ccbe9821c8b8f19f487cf5bfbbf4067e" -dependencies = [ - "bstr", - "itoa", - "thiserror", - "time", -] - -[[package]] -name = "gix-features" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d46a4a5c6bb5bebec9c0d18b65ada20e6517dbd7cf855b87dd4bbdce3a771b2" -dependencies = [ - "gix-hash", - "gix-trace", - "libc", - "prodash", - "sha1_smol", - "walkdir", -] - -[[package]] -name = "gix-fs" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e86eb040f5776a5ade092282e51cdcad398adb77d948b88d17583c2ae4e107" -dependencies = [ - "gix-features", -] - -[[package]] -name = "gix-glob" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db19298c5eeea2961e5b3bf190767a2d1f09b8802aeb5f258e42276350aff19" -dependencies = [ - "bitflags 2.4.1", - "bstr", - "gix-features", - "gix-path", -] - -[[package]] -name = "gix-hash" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8cf8c2266f63e582b7eb206799b63aa5fa68ee510ad349f637dfe2d0653de0" -dependencies = [ - "faster-hex", - "thiserror", -] - -[[package]] -name = "gix-lock" -version = "11.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5c65e6a29830a435664891ced3f3c1af010f14900226019590ee0971a22f37" -dependencies = [ - "gix-tempfile", - "gix-utils", - "thiserror", -] - -[[package]] -name = "gix-object" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740f2a44267f58770a1cb3a3d01d14e67b089c7136c48d4bddbb3cfd2bf86a51" -dependencies = [ - "bstr", - "btoi", - "gix-actor", - "gix-date", - "gix-features", - "gix-hash", - "gix-validate", - "itoa", - "smallvec", - "thiserror", - "winnow", -] - -[[package]] -name = "gix-path" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dd0998ab245f33d40ca2267e58d542fe54185ebd1dc41923346cf28d179fb6" -dependencies = [ - "bstr", - "gix-trace", - "home", - "once_cell", - "thiserror", -] - -[[package]] -name = "gix-ref" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec2f6d07ac88d2fb8007ee3fa3e801856fb9d82e7366ec0ca332eb2c9d74a52" -dependencies = [ - "gix-actor", - "gix-date", - "gix-features", - "gix-fs", - "gix-hash", - "gix-lock", - "gix-object", - "gix-path", - "gix-tempfile", - "gix-validate", - "memmap2", - "thiserror", - "winnow", -] - -[[package]] -name = "gix-sec" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f6dce0c6683e2219e8169aac4b1c29e89540a8262fef7056b31d80d969408c" -dependencies = [ - "bitflags 2.4.1", - "gix-path", - "libc", - "windows", -] - -[[package]] -name = "gix-tempfile" -version = "11.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388dd29114a86ec69b28d1e26d6d63a662300ecf61ab3f4cc578f7d7dc9e7e23" -dependencies = [ - "gix-fs", - "libc", - "once_cell", - "parking_lot", - "tempfile", -] - -[[package]] -name = "gix-trace" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e1127ede0475b58f4fe9c0aaa0d9bb0bad2af90bbd93ccd307c8632b863d89" - -[[package]] -name = "gix-utils" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6225e2de30b6e9bca2d9f1cc4731640fcef0fb3cabddceee366e7e85d3e94f" -dependencies = [ - "fastrand 2.0.1", -] - -[[package]] -name = "gix-validate" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7cc36f496bd5d96cdca0f9289bb684480725d40db60f48194aa7723b883854" -dependencies = [ - "bstr", - "thiserror", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "globset" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax 0.8.2", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "h2" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 2.1.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "http" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "human-panic" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a79a67745be0cb8dd2771f03b24c2f25df98d5471fe7a595d668cfa2e6f843d" -dependencies = [ - "anstream", - "anstyle", - "backtrace", - "os_info", - "serde", - "serde_derive", - "toml 0.8.8", - "uuid", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.5", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.3", - "serde", -] - -[[package]] -name = "indicatif" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" -dependencies = [ - "console", - "instant", - "number_prefix", - "portable-atomic", - "unicode-width", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "intrusive-collections" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b694dc9f70c3bda874626d2aed13b780f137aab435f4e9814121955cf706122e" -dependencies = [ - "memoffset 0.9.0", -] - -[[package]] -name = "inventory" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8573b2b1fb643a372c73b23f4da5f888677feef3305146d68a539250a9bccc7" - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.3", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "is-terminal" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" -dependencies = [ - "hermit-abi 0.3.3", - "rustix 0.38.28", - "windows-sys 0.52.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" - -[[package]] -name = "jobserver" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "keyring" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b52a1d320b55eacc02d4561fed9714af4e98b7989cf4e696bee192b03fc99720" -dependencies = [ - "byteorder", - "lazy_static", - "linux-keyutils", - "secret-service", - "security-framework", - "winapi", -] - -[[package]] -name = "kstring" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" -dependencies = [ - "serde", - "static_assertions", -] - -[[package]] -name = "lalrpop" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" -dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools 0.10.5", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax 0.7.5", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "libc" -version = "0.2.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" - -[[package]] -name = "libgit2-sys" -version = "0.16.1+1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall", -] - -[[package]] -name = "libssh2-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-keyutils" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f27bb67f6dd1d0bb5ab582868e4f65052e58da6401188a08f0da09cf512b84b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" - -[[package]] -name = "liquid" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f68ae1011499ae2ef879f631891f21c78e309755f4a5e483c4a8f12e10b609" -dependencies = [ - "doc-comment", - "liquid-core", - "liquid-derive", - "liquid-lib", - "serde", -] - -[[package]] -name = "liquid-core" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e0724dfcaad5cfb7965ea0f178ca0870b8d7315178f4a7179f5696f7f04d5f" -dependencies = [ - "anymap2", - "itertools 0.10.5", - "kstring", - "liquid-derive", - "num-traits", - "pest", - "pest_derive", - "regex", - "serde", - "time", -] - -[[package]] -name = "liquid-derive" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2fb41a9bb4257a3803154bdf7e2df7d45197d1941c9b1a90ad815231630721" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "liquid-lib" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a17e273a6fb1fb6268f7a5867ddfd0bd4683c7e19b51084f3d567fad4348c0" -dependencies = [ - "itertools 0.10.5", - "liquid-core", - "once_cell", - "percent-encoding", - "regex", - "time", - "unicode-segmentation", -] - -[[package]] -name = "lit" -version = "1.0.4" -source = "git+https://github.com/bitwalker/lit?branch=main#a4f29aba5023d66cce64918334fc5c8af68f0ae7" -dependencies = [ - "clap 2.34.0", - "error-chain", - "itertools 0.10.5", - "lazy_static", - "log", - "regex", - "tempfile", - "term", - "walkdir", -] - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "logos" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" -dependencies = [ - "logos-derive", -] - -[[package]] -name = "logos-codegen" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" -dependencies = [ - "beef", - "fnv", - "proc-macro2", - "quote", - "regex-syntax 0.6.29", - "syn 2.0.45", -] - -[[package]] -name = "logos-derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" -dependencies = [ - "logos-codegen", -] - -[[package]] -name = "memchr" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" - -[[package]] -name = "memmap2" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miden-air" -version = "0.8.0" -source = "git+https://github.com/0xPolygonMiden/miden-vm?branch=next#e1dfdd9699936923a2c28e126a0ed65dcb8faebd" -dependencies = [ - "miden-core", - "winter-air", -] - -[[package]] -name = "miden-assembly" -version = "0.8.0" -source = "git+https://github.com/0xPolygonMiden/miden-vm?branch=next#e1dfdd9699936923a2c28e126a0ed65dcb8faebd" -dependencies = [ - "miden-core", - "num_enum", -] - -[[package]] -name = "miden-codegen-masm" -version = "0.1.0" -dependencies = [ - "anyhow", - "cranelift-entity", - "env_logger 0.9.3", - "intrusive-collections", - "inventory", - "log", - "miden-assembly", - "miden-diagnostics", - "miden-hir", - "miden-hir-analysis", - "miden-hir-transform", - "midenc-session", - "paste", - "petgraph", - "proptest", - "rustc-hash", - "smallvec", - "thiserror", -] - -[[package]] -name = "miden-core" -version = "0.8.0" -source = "git+https://github.com/0xPolygonMiden/miden-vm?branch=next#e1dfdd9699936923a2c28e126a0ed65dcb8faebd" -dependencies = [ - "miden-crypto", - "winter-crypto", - "winter-math", - "winter-utils", -] - -[[package]] -name = "miden-crypto" -version = "0.8.0" -source = "git+https://github.com/0xPolygonMiden/crypto?branch=next#290894f49728ae91ad821aa78ee9c373d1232dd0" -dependencies = [ - "blake3", - "cc", - "glob", - "libc", - "winter-crypto", - "winter-math", - "winter-utils", -] - -[[package]] -name = "miden-diagnostics" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3a82597c2a9babcff4c9283a95130a96aaf8e339954a083bb6582fc2520cf1" -dependencies = [ - "atty", - "codespan", - "codespan-reporting", - "flurry", - "miden-diagnostics-macros", - "parking_lot", - "rustc-hash", - "unicode-width", -] - -[[package]] -name = "miden-diagnostics-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "491d10b0eb201ba767ccdf69bf77f9d5662caf55e9ef468264cccb7129edff62" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "miden-frontend-wasm" -version = "0.1.0" -dependencies = [ - "anyhow", - "derive_more", - "expect-test", - "gimli", - "indexmap 2.1.0", - "log", - "miden-diagnostics", - "miden-hir", - "miden-hir-type", - "miden-integration-tests", - "smallvec", - "thiserror", - "wasmparser 0.118.1", - "wat", -] - -[[package]] -name = "miden-hir" -version = "0.1.0" -dependencies = [ - "anyhow", - "cranelift-entity", - "intrusive-collections", - "inventory", - "lalrpop", - "lalrpop-util", - "miden-assembly", - "miden-diagnostics", - "miden-hir-macros", - "miden-hir-symbol", - "miden-hir-type", - "miden-parsing", - "midenc-session", - "num-bigint", - "num-traits", - "paste", - "petgraph", - "pretty_assertions", - "rustc-hash", - "smallvec", - "thiserror", - "typed-arena", - "winter-math", -] - -[[package]] -name = "miden-hir-analysis" -version = "0.1.0" -dependencies = [ - "anyhow", - "cranelift-bforest", - "cranelift-entity", - "inventory", - "miden-diagnostics", - "miden-hir", - "midenc-session", - "rustc-hash", - "smallvec", - "thiserror", -] - -[[package]] -name = "miden-hir-macros" -version = "0.1.0" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "miden-hir-symbol" -version = "0.1.0" -dependencies = [ - "Inflector", - "rustc-hash", - "toml 0.5.11", -] - -[[package]] -name = "miden-hir-transform" -version = "0.1.0" -dependencies = [ - "anyhow", - "inventory", - "miden-diagnostics", - "miden-hir", - "miden-hir-analysis", - "midenc-session", - "pretty_assertions", - "rustc-hash", - "smallvec", -] - -[[package]] -name = "miden-hir-type" -version = "0.1.0" -dependencies = [ - "smallvec", -] - -[[package]] -name = "miden-integration-tests" -version = "0.1.0" -dependencies = [ - "cargo_metadata", - "concat-idents", - "expect-test", - "miden-assembly", - "miden-codegen-masm", - "miden-core", - "miden-diagnostics", - "miden-frontend-wasm", - "miden-hir", - "miden-hir-transform", - "miden-integration-tests-rust-fib", - "miden-processor", - "miden-stdlib", - "midenc-session", - "proptest", - "rustc-demangle", - "sha2", - "wasmprinter", -] - -[[package]] -name = "miden-integration-tests-rust-fib" -version = "0.1.0" - -[[package]] -name = "miden-parsing" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36dfec2c0319b3773a83627318f92a0212077bed80148c86e8b09af60cd1a88" -dependencies = [ - "miden-diagnostics", - "thiserror", -] - -[[package]] -name = "miden-processor" -version = "0.8.0" -source = "git+https://github.com/0xPolygonMiden/miden-vm?branch=next#e1dfdd9699936923a2c28e126a0ed65dcb8faebd" -dependencies = [ - "log", - "miden-air", - "miden-core", - "winter-prover", -] - -[[package]] -name = "miden-stdlib" -version = "0.8.0" -source = "git+https://github.com/0xPolygonMiden/miden-vm?branch=next#e1dfdd9699936923a2c28e126a0ed65dcb8faebd" -dependencies = [ - "miden-assembly", -] - -[[package]] -name = "midenc" -version = "0.1.0" -dependencies = [ - "anyhow", - "env_logger 0.9.3", - "human-panic", - "midenc-driver", -] - -[[package]] -name = "midenc-compile" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 4.4.12", - "inventory", - "log", - "miden-assembly", - "miden-codegen-masm", - "miden-diagnostics", - "miden-frontend-wasm", - "miden-hir", - "miden-hir-transform", - "midenc-session", - "rustc-hash", - "thiserror", -] - -[[package]] -name = "midenc-driver" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 4.4.12", - "miden-diagnostics", - "miden-hir", - "midenc-compile", - "midenc-session", - "thiserror", -] - -[[package]] -name = "midenc-session" -version = "0.1.0" -dependencies = [ - "atty", - "clap 4.4.12", - "inventory", - "miden-diagnostics", - "miden-hir-symbol", - "rustc-hash", - "thiserror", -] - -[[package]] -name = "miette" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" -dependencies = [ - "miette-derive", - "once_cell", - "thiserror", - "unicode-width", -] - -[[package]] -name = "miette-derive" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "names" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" -dependencies = [ - "rand", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", -] - -[[package]] -name = "normpath" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "num" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.3", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" -dependencies = [ - "proc-macro-crate 2.0.0", - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -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.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" -dependencies = [ - "bitflags 2.4.1", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "os_info" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" -dependencies = [ - "log", - "serde", - "winapi", -] - -[[package]] -name = "owo-colors" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.5", -] - -[[package]] -name = "parse_arg" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14248cc8eced350e20122a291613de29e4fa129ba2731818c4cdbb44fccd3e55" - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "path-absolutize" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5" -dependencies = [ - "path-dedot", -] - -[[package]] -name = "path-dedot" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" -dependencies = [ - "once_cell", -] - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "pbjson" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048f9ac93c1eab514f9470c4bc8d97ca2a0a236b84f45cc19d69a59fc11467f6" -dependencies = [ - "base64 0.13.1", - "serde", -] - -[[package]] -name = "pbjson-build" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbb7b706f2afc610f3853550cdbbf6372fd324824a087806bd4480ea4996e24" -dependencies = [ - "heck", - "itertools 0.10.5", - "prost", - "prost-types", -] - -[[package]] -name = "pbjson-types" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88c8d87f99a4ac14325e7a4c24af190fca261956e3b82dd7ed67e77e6c7043" -dependencies = [ - "bytes", - "chrono", - "pbjson", - "pbjson-build", - "prost", - "prost-build", - "serde", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "pest_meta" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - -[[package]] -name = "petgraph" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" -dependencies = [ - "fixedbitset", - "indexmap 2.1.0", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "piper" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" -dependencies = [ - "atomic-waker", - "fastrand 2.0.1", - "futures-io", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" - -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" -dependencies = [ - "cfg-if", - "concurrent-queue", - "pin-project-lite", - "rustix 0.38.28", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "portable-atomic" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "pretty_assertions" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" -dependencies = [ - "diff", - "yansi", -] - -[[package]] -name = "pretty_env_logger" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" -dependencies = [ - "env_logger 0.10.1", - "log", -] - -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", -] - -[[package]] -name = "proc-macro2" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prodash" -version = "26.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf" - -[[package]] -name = "proptest" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.4.1", - "lazy_static", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax 0.8.2", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck", - "itertools 0.10.5", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-reflect" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b823de344848e011658ac981009100818b322421676740546f8b52ed5249428" -dependencies = [ - "logos", - "miette", - "once_cell", - "prost", - "prost-types", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost", -] - -[[package]] -name = "protox" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a5aacd1f6147ceac5e3896e0c766187dc6a9645f3b93ec821fabbaf821b887" -dependencies = [ - "bytes", - "miette", - "prost", - "prost-reflect", - "prost-types", - "protox-parse", - "thiserror", -] - -[[package]] -name = "protox-parse" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30fc6d0af2dec2c39da31eb02cc78cbc05b843b04f30ad78ccc6e8a342ec5518" -dependencies = [ - "logos", - "miette", - "prost-types", - "thiserror", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "remove_dir_all" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23895cfadc1917fed9c6ed76a8c2903615fa3704f7493ff82b364c6540acc02b" -dependencies = [ - "aligned", - "cfg-if", - "cvt", - "fs_at", - "lazy_static", - "libc", - "normpath", - "windows-sys 0.45.0", -] - -[[package]] -name = "reqwest" -version = "0.11.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" -dependencies = [ - "base64 0.21.5", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rhai" -version = "1.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3625f343d89990133d013e39c46e350915178cf94f1bec9f49b0cbef98a3e3c" -dependencies = [ - "ahash 0.8.7", - "bitflags 2.4.1", - "instant", - "num-traits", - "once_cell", - "rhai_codegen", - "smallvec", - "smartstring", -] - -[[package]] -name = "rhai_codegen" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853977598f084a492323fe2f7896b4100a86284ee8473612de60021ea341310f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "rpassword" -version = "7.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" -dependencies = [ - "libc", - "rtoolbox", - "windows-sys 0.48.0", -] - -[[package]] -name = "rtoolbox" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" -dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys 0.4.12", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "ryu" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "sanitize-filename" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "secrecy" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" -dependencies = [ - "zeroize", -] - -[[package]] -name = "secret-service" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da1a5ad4d28c03536f82f77d9f36603f5e37d8869ac98f0a750d5b5686d8d95" -dependencies = [ - "aes", - "block-modes", - "futures-util", - "generic-array", - "hkdf", - "num", - "once_cell", - "rand", - "serde", - "sha2", - "zbus", -] - -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "seize" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e5739de653b129b0a59da381599cf17caf24bc586f6a797c52d3d6147c5b85a" -dependencies = [ - "num_cpus", - "once_cell", -] - -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" -dependencies = [ - "serde", -] - -[[package]] -name = "serde" -version = "1.0.193" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.193" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "serde_json" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" -dependencies = [ - "base64 0.21.5", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.1.0", - "serde", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - -[[package]] -name = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "spdx" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" -dependencies = [ - "smallvec", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eae3c679c56dc214320b67a1bc04ef3dfbd6411f6443974b5e4893231298e66" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" -dependencies = [ - "cfg-if", - "fastrand 2.0.1", - "redox_syscall", - "rustix 0.38.28", - "windows-sys 0.48.0", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "terminal-prompt" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572818b3472910acbd5dff46a3413715c18e934b071ab2ba464a7b2c2af16376" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "time" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" -dependencies = [ - "deranged", - "itoa", - "libc", - "num_threads", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" -dependencies = [ - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.35.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.5", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "indexmap 1.9.3", - "serde", -] - -[[package]] -name = "toml" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" -dependencies = [ - "indexmap 2.1.0", - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.21.0", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" -dependencies = [ - "indexmap 2.1.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - -[[package]] -name = "uds_windows" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" -dependencies = [ - "memoffset 0.9.0", - "tempfile", - "winapi", -] - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-bidi" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" - -[[package]] -name = "unicode-bom" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" -dependencies = [ - "getrandom", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "waker-fn" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" - -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "warg-api" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6155f45f54e013e7e4016ed728d132e5b5e4da6a9ab688edfa0a19a3d6fe87f6" -dependencies = [ - "itertools 0.11.0", - "serde", - "serde_with", - "thiserror", - "warg-crypto", - "warg-protocol", -] - -[[package]] -name = "warg-client" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "807a117360b22b04a5e89adf980d48f0076fc28c6d971bf3a868cce29b76a29a" -dependencies = [ - "anyhow", - "async-trait", - "bytes", - "clap 4.4.12", - "dirs", - "futures-util", - "itertools 0.11.0", - "libc", - "normpath", - "once_cell", - "pathdiff", - "reqwest", - "serde", - "serde_json", - "tempfile", - "thiserror", - "tokio", - "tokio-util", - "tracing", - "url", - "walkdir", - "warg-api", - "warg-crypto", - "warg-protocol", - "warg-transparency", - "windows-sys 0.48.0", -] - -[[package]] -name = "warg-crypto" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3421fa178ce468087d2c51b53ae4ab6d435e4e41bb6fee2c2b25bd555df9f6c3" -dependencies = [ - "anyhow", - "base64 0.21.5", - "digest", - "hex", - "leb128", - "once_cell", - "p256", - "rand_core", - "secrecy", - "serde", - "sha2", - "signature", - "thiserror", -] - -[[package]] -name = "warg-protobuf" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a6adafed625d8d254d34ee0c9836c2f9e6bd5a799b489ffc31c2c572554234" -dependencies = [ - "anyhow", - "pbjson", - "pbjson-build", - "pbjson-types", - "prost", - "prost-build", - "prost-types", - "protox", - "regex", - "serde", - "warg-crypto", -] - -[[package]] -name = "warg-protocol" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e47b7e7a8a9f9895268b68e7cf29ca1d3129faa9f9bcbc973ff5e92884a014" -dependencies = [ - "anyhow", - "base64 0.21.5", - "hex", - "indexmap 2.1.0", - "pbjson-types", - "prost", - "prost-types", - "semver", - "serde", - "serde_with", - "thiserror", - "warg-crypto", - "warg-protobuf", - "warg-transparency", - "wasmparser 0.108.0", -] - -[[package]] -name = "warg-transparency" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cd84da2c4f298556706e75a8f19df3e2c844be2ce9e959b07904fb66c333cc" -dependencies = [ - "anyhow", - "prost", - "thiserror", - "warg-crypto", - "warg-protobuf", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.45", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" - -[[package]] -name = "wasm-encoder" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-metadata" -version = "0.10.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a" -dependencies = [ - "anyhow", - "indexmap 2.1.0", - "serde", - "serde_derive", - "serde_json", - "spdx", - "wasm-encoder", - "wasmparser 0.118.1", -] - -[[package]] -name = "wasm-streams" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasmparser" -version = "0.108.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c956109dcb41436a39391139d9b6e2d0a5e0b158e1293ef352ec977e5e36c5" -dependencies = [ - "indexmap 2.1.0", - "semver", -] - -[[package]] -name = "wasmparser" -version = "0.118.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" -dependencies = [ - "indexmap 2.1.0", - "semver", -] - -[[package]] -name = "wasmprinter" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d027eb8294904fc715ac0870cebe6b0271e96b90605ee21511e7565c4ce568c" -dependencies = [ - "anyhow", - "wasmparser 0.118.1", -] - -[[package]] -name = "wast" -version = "69.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ee37317321afde358e4d7593745942c48d6d17e0e6e943704de9bbee121e7a" -dependencies = [ - "leb128", - "memchr", - "unicode-width", - "wasm-encoder", -] - -[[package]] -name = "wat" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb338ee8dee4d4cd05e6426683f21c5087dc7cfc8903e839ccf48d43332da3c" -dependencies = [ - "wast", -] - -[[package]] -name = "web-sys" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.28", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets 0.52.0", -] - -[[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", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -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", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -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", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -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" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -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" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -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" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -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" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -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" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "winnow" -version = "0.5.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "winter-air" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de512b0b63764cfe8c0bf4372e7ac7571f8bca0bb780091cdcbecd1fb320e436" -dependencies = [ - "libm", - "winter-crypto", - "winter-fri", - "winter-math", - "winter-utils", -] - -[[package]] -name = "winter-crypto" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d832015262e7d8ce6c160d3c8eb52e3a1c1352a7760523a25c4094c5d250cb9" -dependencies = [ - "blake3", - "sha3", - "winter-math", - "winter-utils", -] - -[[package]] -name = "winter-fri" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3769f453f00738ba761ce94b63e7fc5a08903ab1d8c3e2a27c46d4327290cd0" -dependencies = [ - "winter-crypto", - "winter-math", - "winter-utils", -] - -[[package]] -name = "winter-math" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f51c43e0756c89607b74a2da621ca472306bfb1ecc352a44a97dfe73cd0503d" -dependencies = [ - "winter-utils", -] - -[[package]] -name = "winter-prover" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd3744a539c4b9f92ba43123a5eed5b0f6402204f92c6a3187de30189693e7b" -dependencies = [ - "log", - "winter-air", - "winter-crypto", - "winter-fri", - "winter-math", - "winter-utils", -] - -[[package]] -name = "winter-utils" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58f2e14e2223fdc75146ba65bdac6e27a4f820029ec2d6a183e6fba033622e9" - -[[package]] -name = "wit-bindgen-core" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dfc34e539edf78da2efed167549962a4d26000db04694408285f52d90703fee" -dependencies = [ - "anyhow", - "wit-component", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e898ad170a2796e9ecc495ce52cc2518dc35364ec042a4e108dd3c454e2a24c6" -dependencies = [ - "anyhow", - "heck", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-component" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2" -dependencies = [ - "anyhow", - "bitflags 2.4.1", - "indexmap 2.1.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser 0.118.1", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.1.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", -] - -[[package]] -name = "xdg-home" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" -dependencies = [ - "nix", - "winapi", -] - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - -[[package]] -name = "zbus" -version = "3.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io 1.13.0", - "async-lock 2.8.0", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "byteorder", - "derivative", - "enumflags2", - "event-listener 2.5.3", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix", - "once_cell", - "ordered-stream", - "rand", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "winapi", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "3.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" -dependencies = [ - "serde", - "static_assertions", - "zvariant", -] - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" - -[[package]] -name = "zvariant" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" -dependencies = [ - "byteorder", - "enumflags2", - "libc", - "serde", - "static_assertions", - "zvariant_derive", -] - -[[package]] -name = "zvariant_derive" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 14763118f..000000000 --- a/Cargo.toml +++ /dev/null @@ -1,98 +0,0 @@ -[workspace] -resolver = "2" -members = [ - "codegen/*", - "hir", - "hir-analysis", - "hir-macros", - "hir-symbol", - "hir-transform", - "hir-type", - "midenc", - "midenc-compile", - "midenc-driver", - "midenc-session", - "tools/*", - "frontend-wasm", - "tests/rust-apps/*", - "tests/integration", -] -exclude = ["tests/rust-apps-wasm", "cargo-ext/tests/data"] - -[workspace.package] -version = "0.1.0" -rust-version = "1.71" -authors = ["Miden Team"] -description = "An intermediate representation and compiler for Miden Assembly" -repository = "https://github.com/0xPolygonMiden/compiler" -homepage = "https://github.com/0xPolygonMiden/compiler" -documentation = "https://github.com/0xPolygonMiden/compiler" -categories = ["Compilers"] -keywords = ["compiler", "miden"] -license = "MIT" -readme = "README.md" -edition = "2021" -publish = false - -[workspace.dependencies] -anyhow = "1.0" -bitflags = "1.3" -clap = { version = "4.1", features = ["derive", "env"] } -cranelift-entity = "0.100" -cranelift-bforest = "0.100" -env_logger = "0.9" -either = "1.9" -Inflector = "0.11" -intrusive-collections = "0.9" -inventory = "0.3" -log = "0.4" -paste = "1.0" -parking_lot = "0.12" -parking_lot_core = "0.9" -petgraph = "0.6" -pretty_assertions = "1.0" -proptest = "1.4" -rustc-hash = "1.1" -smallvec = { version = "1.9", features = [ - "union", - "const_generics", - "const_new", -] } -smallstr = { version = "0.3", features = ["union"] } -thiserror = "1.0" -toml = { version = "0.5", features = ["preserve_order"] } -miden-assembly = { git = "https://github.com/0xPolygonMiden/miden-vm", branch = "next" } -miden-core = { git = "https://github.com/0xPolygonMiden/miden-vm", branch = "next" } -miden-processor = { git = "https://github.com/0xPolygonMiden/miden-vm", branch = "next" } -miden-stdlib = { git = "https://github.com/0xPolygonMiden/miden-vm", branch = "next" } -miden-codegen-masm = { path = "codegen/masm" } -miden-diagnostics = "0.1" -miden-hir = { path = "hir" } -miden-hir-analysis = { path = "hir-analysis" } -miden-hir-macros = { path = "hir-macros" } -miden-hir-symbol = { path = "hir-symbol" } -miden-hir-transform = { path = "hir-transform" } -miden-hir-type = { path = "hir-type" } -miden-parsing = "0.1" -miden-frontend-wasm = { path = "frontend-wasm" } -midenc-compile = { path = "midenc-compile" } -midenc-driver = { path = "midenc-driver" } -midenc-session = { path = "midenc-session" } -miden-integration-tests = { path = "tests/integration" } - -[profile.dev] -lto = false -# Needed for 'inventory' to work -codegen-units = 1 - -[profile.release] -opt-level = 2 -debug = true -codegen-units = 1 -lto = "thin" - -[profile.test.package.proptest] -opt-level = 3 - -[profile.test.package.rand_chacha] -opt-level = 3 diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index c6fe12c86..000000000 --- a/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 Polygon Miden - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/Makefile.toml b/Makefile.toml deleted file mode 100644 index a4b230ef8..000000000 --- a/Makefile.toml +++ /dev/null @@ -1,243 +0,0 @@ -[config] -default_to_workspace = false -skip_core_tasks = true -skip_git_env_info = true -modify_core_tasks = { private = true, namespace = "default" } -init_task = "init" - -[env] -CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true -CARGO_MAKE_CARGO_VERBOSE_FLAGS = { source = "${CARGO_MAKE_CI}", default_value = "", mapping = { "true" = "--verbose" } } -CARGO_MAKE_RUST_SCRIPT_PROVIDER = "rust-script" -CARGO_MAKE_USE_WORKSPACE_PROFILE = true -CARGO_MAKE_CARGO_BUILD_TEST_FLAGS = "--no-fail-fast" -CARGO_TARGET_DIR = { value = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } } -BACKTRACE_DEFAULT = { source = "${CARGO_MAKE_CI}", mapping = { "true" = "1", "false" = "0" } } -RUST_BACKTRACE = { value = "${BACKTRACE_DEFAULT}", condition = { env_not_set = ["RUST_BACKTRACE"] } } -MIDEN_BIN_DIR = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/bin" -MIDEN_INSTALL_DIR = "${MIDEN_BIN_DIR}/${CARGO_MAKE_RUST_TARGET_TRIPLE}" - -[tasks.init] -run_task = "print-env" - -[tasks.default] -category = "Build" -description = "Default task builds the compiler" -alias = "build" - -[tasks.print-env] -category = "Tools" -run_task = [{ name = ["print-build-env", "print-ci-env", "print-rust-env", "print-cargo-env"] }] - -[tasks.print-build-env] -private = true -script = [ -''' -#!@duckscript -echo "*************************************" -echo "Build Environment:" -echo " Task: ${CARGO_MAKE_TASK}" -echo " Task Arguments: ${CARGO_MAKE_TASK_ARGS}" -echo " Command: ${CARGO_MAKE_COMMAND}" -echo " Working Directory: ${CARGO_MAKE_WORKING_DIRECTORY}" -echo " Target Directory: ${CARGO_TARGET_DIR}" -echo " Profile: ${CARGO_MAKE_PROFILE}" -echo " Bin Directory: ${MIDEN_BIN_DIR}" -echo " Install Directory: ${MIDEN_INSTALL_DIR}" -echo " Target Triple: ${CARGO_MAKE_RUST_TARGET_TRIPLE}" -echo " RUST_BACKTRACE: ${RUST_BACKTRACE}" -echo "*************************************" -''' -] - -[tasks.print-ci-env] -private = true -condition = { env = { "CARGO_MAKE_CI" = "true" } } -script = [ -''' -#!@duckscript -echo "*************************************" -echo "CI:" -echo " CI: ${CARGO_MAKE_CI}" -echo " PR: ${CARGO_MAKE_PR}" -echo " Branch Name: ${CARGO_MAKE_CI_BRANCH_NAME}" -echo " CI Vendor: ${CARGO_MAKE_CI_VENDOR}" -echo "*************************************" -''' -] - -[tasks.print-rust-env] -category = "Tools" -condition = { env_set = [ "CARGO_MAKE_RUST_CHANNEL" ] } -script = [ -''' -#!@duckscript -echo "*************************************" -echo "Rust:" -echo " Version: ${CARGO_MAKE_RUST_VERSION}" -echo " Channel: ${CARGO_MAKE_RUST_CHANNEL}" -echo " Target Arch: ${CARGO_MAKE_RUST_TARGET_ARCH}" -echo " Target Env: ${CARGO_MAKE_RUST_TARGET_ENV}" -echo " Target OS: ${CARGO_MAKE_RUST_TARGET_OS}" -echo " Pointer Width: ${CARGO_MAKE_RUST_TARGET_POINTER_WIDTH}" -echo " Target Triple: ${CARGO_MAKE_RUST_TARGET_TRIPLE}" -echo "*************************************" -''' -] - -[tasks.print-cargo-env] -category = "Tools" -condition = { env_set = [ "CARGO_MAKE_CARGO_HOME" ] } -script = [ -''' -#!@duckscript -echo "*************************************" -echo "Cargo:" -echo " Home: ${CARGO_MAKE_CARGO_HOME}" -echo " Profile: ${CARGO_MAKE_CARGO_PROFILE}" -echo "*************************************" -''' -] - -[tasks.format] -category = "Development" -dependencies = ["format-rust"] - -[tasks.check-format] -description = "Runs cargo fmt to check appropriate code format." -category = "Test" -command = "cargo" -args = ["fmt", "--", "--check"] -dependencies = ["install-rustfmt"] - -[tasks.format-rust] -category = "Development" -description = "Formats source code (Rust)" -command = "cargo" -args = ["fmt"] -dependencies = ["install-rustfmt"] - -[tasks.install-rustfmt] -category = "Development" -description = "Installs cargo rustfmt plugin." -env.CFG_RELEASE = { value = "${CARGO_MAKE_RUST_VERSION}", condition = { env_not_set = ["CFG_RELEASE"] } } -env.CFG_RELEASE_CHANNEL = { value = "${CARGO_MAKE_RUST_CHANNEL}", condition = { env_not_set = ["CFG_RELEASE_CHANNEL"] } } -install_crate = { crate_name = "rustfmt-nightly", rustup_component_name = "rustfmt", binary = "rustfmt", test_arg = "--help" } - -[tasks.bloat] -category = "Development" -description = "Run cargo-bloat" -command = "cargo" -args = ["bloat", "${@}"] - -[tasks.midenc] -category = "Build" -description = "Builds midenc and installs it to the bin folder" -command = "cargo" -args = ["-Z", "unstable-options", "build", "-p", "midenc", "--out-dir", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/bin"] - -[tasks.build] -category = "Build" -description = "Runs cargo build on the workspace" -run_task = [ - { name = ["midenc", "build-filecheck"] }, -] - -[tasks.install] -category = "Install" -description = "Installs the compiler suite via cargo" -run_task = [ - { name = ["install-midenc"] }, -] - -[tasks.check] -category = "Build" -description = "Runs cargo check on the workspace" -command = "cargo" -args = ["check", "${@}"] - -[tasks.clean] -category = "Build" -description = "Clean build artifacts" -command = "cargo" -args = ["clean", "${@}"] - -[tasks.test] -category = "Test" -description = "Runs all tests" -dependencies = ["test-rust", "test-filecheck"] - -[tasks.install-midenc] -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", "midenc"] - -[tasks.install-wasm-target] -category = "Test" -description = "Install wasm32-unknown-unknown target" -command = "rustup" -args = ["target", "add", "wasm32-unknown-unknown"] - -[tasks.install-wasm-wasi-target] -category = "Test" -description = "Install wasm32-wasi target" -command = "rustup" -args = ["target", "add", "wasm32-wasi"] - -[tasks.install-rust-src] -category = "Test" -description = "Install rust-src component" -command = "rustup" -args = ["component", "add", "rust-src"] - -[tasks.install-cargo-component] -category = "Test" -description = "Install cargo-component extension" -command = "cargo" -args = ["install", "cargo-component"] - -[tasks.test-rust] -category = "Test" -description = "Runs tests written in Rust" -command = "cargo" -args = ["test", "@@remove-empty(CARGO_MAKE_CARGO_VERBOSE_FLAGS)", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )", "${@}"] -dependencies = ["install-wasm-target", "install-wasm-wasi-target", "install-rust-src", "install-cargo-component"] - -[tasks.test-filecheck] -category = "Test" -description = "Runs file-based checks using filecheck" -command = "bin/filecheck" -args = ["-t", "hir", "-t", "sw", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/tests/lit"] -dependencies = [ - "build-filecheck", -] - -[tasks.filecheck] -category = "Test" -description = "Invokes filecheck with the given arguments" -command = "bin/filecheck" -args = ["${@}"] -dependencies = [ - "build", -] - -[tasks.build-filecheck] -category = "Build" -description = "Builds the filecheck helper" -command = "cargo" -args = ["-Z", "unstable-options", "build", "-p", "filecheck", "--release", "--out-dir", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/bin"] - -[tasks.book] -category = "Build" -description = "Builds the compiler documentation" -install_crate = { crate_name = "mdbook", binary = "mdbook", test_arg = ["--help"] } -command = "mdbook" -args = ["build", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/docs"] - -[tasks.serve-book] -category = "Build" -description = "Opens the compiler documentation" -install_crate = { crate_name = "mdbook", binary = "mdbook", test_arg = ["--help"] } -command = "mdbook" -args = ["serve", "--open", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/docs"] diff --git a/README.md b/README.md deleted file mode 100644 index 868c7e816..000000000 --- a/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Miden Compiler - -This repository contains the Miden compiler, which can be used both as a compiler backend -for existing languages that wish to target Miden Assembly using a standard SSA-based IR; -or as means of compiling WebAssembly (Wasm) produced by another compiler to Miden Assembly. - -This repo is broken into the following high-level components: - -* Miden HIR (high-level intermediate representation) and it's supporting crates; -providing everything needed to build and compile IR for a program you want to -emit Miden Assembly for. -* The Wasm frontend; a library which can be used to convert a program compiled to `.wasm` to HIR -* The `midenc` executable, which provides a command-line tool that provides a convenient way -to compile Wasm or HIR modules/programs to Miden Assembly and test them. - -See the `docs` directory for detailed documentation covering the design and implementation -of HIR and the various passes comprising the compilation pipeline. - -This project is a work-in-progress, stay tuned for updates as things develop. - -## Building - -You'll need to have Rust installed (at time of writing, we're doing development against Rust 1.73). - -Additionally, you'll want to have [`cargo-make`](https://github.com/sagiegurari/cargo-make) installed: - - $ cargo install cargo-make - -From there, you can build all of the tooling used for the compiler, including the compiler itself with: - - $ cargo make - -To build just the compiler: - - $ cargo make midenc - -## Testing - -To run the compiler test suite: - - $ cargo make test - -This will run all of the unit tests in the workspace, as well as all of our literate tests, -which are executed by the `filecheck` helper found in the `tools` folder. - -## Packaging - -TBD diff --git a/codegen/masm/Cargo.toml b/codegen/masm/Cargo.toml deleted file mode 100644 index 592ba16a3..000000000 --- a/codegen/masm/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "miden-codegen-masm" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -anyhow.workspace = true -cranelift-entity.workspace = true -intrusive-collections.workspace = true -inventory.workspace = true -log.workspace = true -miden-assembly.workspace = true -miden-diagnostics.workspace = true -miden-hir.workspace = true -miden-hir-analysis.workspace = true -miden-hir-transform.workspace = true -midenc-session.workspace = true -paste.workspace = true -petgraph.workspace = true -rustc-hash.workspace = true -smallvec.workspace = true -thiserror.workspace = true - -[dev-dependencies] -proptest.workspace = true -env_logger.workspace = true diff --git a/codegen/masm/intrinsics/i32.masm b/codegen/masm/intrinsics/i32.masm deleted file mode 100644 index ae259c104..000000000 --- a/codegen/masm/intrinsics/i32.masm +++ /dev/null @@ -1,387 +0,0 @@ -const.SIGN_BIT=2147483648 # 1 << 31 -const.MIN=SIGN_BIT -const.MAX=2147483647 # (1 << 31) - 1 -const.NEG1=4294967295 # u32::MAX - -# Returns `1` if `a` has its sign bit set, else `0` -# -# This function consumes `a`. -export.is_signed # [a] - push.SIGN_BIT u32and push.SIGN_BIT eq -end - -# Get the negation of `a` -# -# This operation is unchecked, so if the input is not a valid i32 the behavior is undefined -export.unchecked_neg # [a] - u32not u32wrapping_add.1 -end - -# Get the negation of `a` -# -# This operation is checked, so if the input is not a valid i32, -# or if the negation is not a valid i32, execution traps -export.checked_neg # [a] - # assert that the negation is representable - dup.0 push.MIN eq assertz - exec.unchecked_neg -end - -# Adds `b` to `a`, asserting that both inputs are valid i32. -# -# Returns the result modulo 2^32, plus a boolean indicating whether or not the subtraction underflowed. -export.overflowing_add # [b, a] - u32assert2 - - # is `b` signed? - dup.0 exec.is_signed # [is_b_signed, b, a] - - # is `a` signed? - dup.2 exec.is_signed # [is_a_signed, is_b_signed, b, a] - - # do both operands have the same sign? - # - # NOTE: We refer to `is_b_signed` as `is_signed` after this, - # because if `is_same_sign` is true, then `is_signed` reflects - # whether both `a` and `b` are signed. If `is_same_sign` is false, - # then overflow is not possible, and the value of `is_b_signed` - # will have no effect on the result - dup.1 eq # [is_same_sign, is_signed, b, a] - - # compute result - movup.3 movup.3 u32wrapping_add - - # is `result` signed? - dup.0 exec.is_signed # [is_result_signed, result, is_same_sign, is_signed] - - # if both operands have the same sign, and the result differs, overflow has occurred - movup.3 neq # [signs_differ, result, is_same_sign] - movup.2 and # [overflowed, result] -end - -# Adds `b` to `a`, wrapping around on overflow. -export.wrapping_add # [b, a] - exec.overflowing_add # [overflowed, result] - drop -end - -# Adds `b` to `a`, asserting on overflow. -export.checked_add # [b, a] - exec.overflowing_add # [overflowed, result] - assertz # [result] -end - -# Subtracts `b` from `a`, asserting that both inputs are valid i32. -# -# Returns the result modulo 2^32, plus a boolean indicating whether or not the subtraction underflowed. -export.overflowing_sub # [b, a] - u32assert2 - - # Subtraction is equivalent to addition if we negate the right-hand operand - # - # However, we must account for the edge case where `i32::MIN` is given, as that - # cannot be negated. In our case, the negation doesn't need to be realized immediately, - # so as long as the result is in range, we don't need to assert. - dup.0 push.MIN eq # [is_b_min, b, a] - if.true - # The following is effectively identical to the implementation for `overflowing_add`, - # but inlined here as we know the value of `b` statically in this branch, and we need - # special handling to account for the fact that `b` was `i32::MIN` - - # NOTE: We treat `b` as unsigned, since we're supposed to be negating it - drop push.MAX # [i32::MAX, a] - dup.1 exec.is_signed # [is_a_signed, i32::MAX, a] - dup.0 eq.0 # [is_same_sign, is_a_signed, i32::MAX, a] - - # compute result - movup.3 movup.3 u32wrapping_add # [a + i32::MAX, is_same_sign, is_a_signed] - push.1 u32wrapping_add # [a + i32::MAX + 1, is_same_sign, is_a_signed] - - # is `result` signed? - dup.0 exec.is_signed # [is_result_signed, result, is_same_sign, is_a_signed] - - # if both operands have the same sign, and the result differs, overflow has occurred - movup.3 neq # [signs_differ, result, is_same_sign] - movup.2 and # [overflowed, result] - else - exec.unchecked_neg - exec.overflowing_add - end -end - -# Subtracts `b` from `a` -# -# This operation will fail if `b` is not a valid i32, or if `result` is not a valid i32 -export.wrapping_sub # [b, a] - exec.overflowing_sub # [overflowed, result] - drop -end - -# Subtracts `b` from `a`, asserting on underflow/overflow -export.checked_sub # [b, a] - exec.overflowing_sub # [overflowed, result] - assertz # [result] -end - -# Multiplies `a` by `b`, asserting that both inputs are valid i32. -# -# Returns the result modulo 2^32, plus a boolean indicating whether or not the multiplication overflowed. -export.overflowing_mul # [b, a] - u32assert2 - - # is `b` i32::MIN? - dup.0 push.MIN eq # [is_b_MIN, b, a] - - # is `a` i32::MIN? - dup.2 push.MIN eq # [is_a_MIN, is_b_MIN, b, a] - - # are either `a` or `b` i32::MIN? - or # [is_either_MIN, b, a] - - # if either operand are MIN, then the following rules apply - # - # 1. If the other operand is 1, then there is no overflow and the result is MIN - # 2. If the other operand is -1, then there is overflow and the result is MIN - # 3. For any other value, there is overflow, and the result is zero - if.true - # if either are 1, rule 1 applies - dup.0 eq.1 # [is_b_1, b, a] - dup.2 eq.1 # [is_a_1, is_b_1, b, a] - or # [is_either_1, b, a] - # either are -1, rule 2 applies - movup.2 push.NEG1 eq # [is_a_neg1, is_either_1, b] - movup.2 push.NEG1 eq # [is_b_neg1, is_a_neg1, is_either_1] - or # [is_either_neg1, is_either_1] - # choose between rule 1/2 or rule 3 result - dup.1 or # [result_is_MIN, is_either_1] - push.MIN push.0 # [0, MIN, result_is_MIN, is_either_1] - swap.2 cdrop # [result, is_either_1] - # overflow occurred if neither operand was 1 - swap.1 not # [overflowed, result] - else - # determine what sign the result should have - # - # 1. If only one operand is negative, the result is negative - # 2. If both operands are positive or negative, the result is positive - dup.0 exec.is_signed # [is_b_signed, b, a] - dup.2 exec.is_signed # [is_a_signed, is_b_signed, b, a] - dup.1 dup.1 neq # [negate_result, is_a_signed, is_b_signed, b, a] - movdn.4 # [is_a_signed, is_b_signed, b, a, negate_result] - - # negate negative operands, use standard unsigned wrapping multiplication, - # then negate the result if the result should be negative - movup.3 dup.0 exec.unchecked_neg # [-a, a, is_a_signed, is_b_signed, b, negate_result] - movup.2 cdrop # [-a or a, is_b_signed, b, negate_result] - swap.2 dup.0 exec.unchecked_neg # [-b, b, is_b_signed, -a or a, negate_result] - movup.2 cdrop # [-b or b, -a or a, negate_result] - u32overflowing_mul # [overflowed, result, negate_result] - - # if the unsigned op overflowed, we definitely overflowed, but overflow - # also occurred if the supposedly unsigned result has its sign bit set, - # which could only happen if we overflowed the positive i32 range - dup.1 exec.is_signed or # [overflowed, result, negate_result] - swap.1 dup.0 exec.unchecked_neg # [-result, result, overflowed, negate_result] - movup.3 cdrop swap.1 # [overflowed, -result or result] - end -end - -# Multiplies `a` by `b`, wrapping on overflow. -export.wrapping_mul # [b, a] - exec.overflowing_mul # [overflowed, result] - drop -end - -# Multiplies `a` by `b`, asserting on overflow -export.checked_mul # [b, a] - exec.overflowing_mul # [overflowed, result] - assertz # [result] -end - -# Divides `a` by `b`, asserting that both inputs are valid i32 -export.checked_div # [b, a] - u32assert2 - - # get positive dividend - dup.1 exec.unchecked_neg # [-a, b, a] - dup.2 swap.1 # [-a, a, b, a] - movup.3 exec.is_signed # [is_a_signed, -a, a, b] - dup.0 movdn.4 cdrop # [|a|, b, is_a_signed] - - # get positive divisor - dup.1 exec.unchecked_neg # [-b, |a|, b, is_a_signed] - dup.2 swap.1 # [-b, b, |a|, b, is_a_signed] - movup.3 exec.is_signed # [is_b_signed, -b, b, |a|, is_a_signed] - dup.0 movdn.5 cdrop # [|b|, |a|, is_a_signed, is_b_signed] - - # divide - u32div # [|a / b|, is_a_signed, is_b_signed] - - # if the signs differ, negate the result - movdn.2 neq # [signs_differ, |a / b|] - dup.1 exec.unchecked_neg # [-|a / b|, signs_differ, |a / b|] - swap.1 cdrop # [result] -end - -# Given two i32 values in two's complement representation, compare them, -# returning -1 if `a` < `b`, 0 if equal, and 1 if `a` > `b`. -export.icmp # [b, a] - dup.1 # [a, b, a] - dup.1 # [b, a, b, a] - - # get the most-significant bit of `b` - push.SIGN_BIT # [1<<31, b, a, b, a] - u32and # [b_msb, a, b, a] - - # get the most-significant bit of `a` - swap.1 # [a, b_msb, b, a] - push.SIGN_BIT # [1<<31, a, b_msb, b, a] - u32and # [a_msb, b_msb, b, a] - - eq.0 # [a_msb == 0, b_msb, b, a] - swap.1 eq.0 # [b_msb == 0, a_msb == 0, b, a] - swap.1 dup.1 neq # [a_msb != b_msb, b_msb == 0, b, a] - - # if a_msb != b_msb, then a > b (if a_msb == 0), or a < b (if a_msb == 1) - if.true # [b_msb == 0, b, a] - movdn.2 drop drop # [b_msb == 0] - push.NEG1 push.1 # [1, -1, b_msb == 0] - swap.2 # [b_msb == 0, -1, 1] - cdrop # [1 or -1] - else # [b_msb == 0, b, a] - # a_msb == b_msb, so we can compare the remaining bits lexicographically, - # which we get for free via the lt/gt ops - drop # [b, a] - dup.1 dup.1 # [b, a, b, a] - u32gt movdn.2 # [b, a, a > b] - u32lt # [a < b, a > b] - push.0 push.NEG1 push.1 - swap.3 # [a < b, -1, 0, 1, a > b] - cdrop # [-1 or 0, 1, a > b] - swap.2 # [a > b, 1, -1 or 0] - cdrop # [1 or -1 or 0] - end -end - -# Given two i32 values in two's complement representation, return 1 if `a < b`, else 0 -export.is_lt # [b, a] - exec.icmp push.NEG1 eq -end - -# Given two i32 values in two's complement representation, return 1 if `a <= b`, else 0 -export.is_lte # [b, a] - exec.icmp neq.1 -end - -# Given two i32 values in two's complement representation, return 1 if `a > b`, else 0 -export.is_gt # [b, a] - exec.icmp eq.1 -end - -# Given two i32 values in two's complement representation, return 1 if `a >= b`, else 0 -export.is_gte # [b, a] - exec.icmp push.NEG1 neq -end - -# Compute 2^n, where `n` must be less than 31, or the result will overflow i32::MAX -export.pow2 # [n] - dup.0 - push.31 - u32lt # [n < 31, pow] - assert # [n] - push.1 swap.1 # [n, 1] - u32shl # [1 << n] -end - -# Compute a^b, where `b` must be a positive i32 value < 31 -export.ipow # [b, a] - dup.0 push.31 u32lt assert # assert that `b` is < 31 - dup.0 eq.0 # [b == 0, b, a] - dup.2 eq.0 # [a == 0, b == 0, b, a] - or # [a == 0 || b == 0, b, a] - # if a == 0, the result is always 0; otherwise if b == 0, then the result is always 1 - if.true - eq.0 # [b == 0, a] - push.1 push.0 # [0, 1, b == 0, a] - swap.2 # [b == 0, 1, 0, a] - cdrop # [1 or 0, a] - swap.1 drop # [1 or 0] - else # [b, a] - # for all other values, we do exponentiation by squaring - push.1 # [acc, b, a] - dup.1 # [b, acc, b, a] - push.1 # [1, b, acc, b, a] - u32gt # [b > 1, acc, b, a] - while.true # [acc, b, a => base] - dup.2 dup.1 # [acc, base, acc, b, base] - u32wrapping_mul # [base * acc, acc, b, base] - dup.2 # [b, base * acc, acc, b, base] - push.1 # [1, b, base * acc, acc, b, base] - u32and # [b & 1, base * acc, acc, b, base] - eq.1 # [b & 1 == 1, base * acc, acc, b, base] - cdrop # [acc, b, base] - swap.1 # [b, acc, base] - u32div.2 # [b /= 2, acc, base] - movup.2 dup.0 # [base, base, b, acc] - u32wrapping_mul # [base * base, b, acc] - swap.1 # [b, base, acc] - movup.2 # [acc, b, base] - dup.1 push.1 # [1, b, acc, b, base] - u32gt # [b > 1, acc, b, base] - end - swap.1 drop # [acc, base] - u32wrapping_mul # [acc * base] - end -end - -# Arithmetic shift-right, i.e. `a >> b` preserves the signedness of the value -# -# This function will assert if `b` is > 31. -# -# This implementation is checked, so it will assert if the inputs are invalid -export.checked_shr # [b, a] - # validate the shift is valid - dup.0 push.32 - u32lt # [b < 32, b, a] - assert - - # if the input is zero, the output is always zero, - # and if the shift is zero, the input is returned unchanged - dup.0 eq.0 # [b == 0, b, a] - dup.2 eq.0 # [a == 0, b == 0, b, a] - or # [a == 0 || b == 0, b, a] - if.true - # return `a` if `b == 0`, otherwise `a == 0` so return 0 - eq.0 # [b == 0, a] - swap.1 push.0 # [0, a, b == 0] - swap.2 # [b == 0, a, 0] - cdrop # [a or 0] - else # [b, a] - # get the signedness of the value - dup.1 # [a, b, a] - push.SIGN_BIT # [1<<31, a, b, a] - u32and push.SIGN_BIT eq # [is_signed, b, a] - - # if the value is signed, we must sign-extend the result, - # otherwise we can treat it as an unsigned shift - if.true # [b, a] - swap.1 # [a, b] - dup.1 # [b, a, b] - u32shr # [shifted, b] - - # compute the extension mask - push.1 dup.2 # [b, 1, shifted, b] - u32shl - sub.1 # [(1 << b) - 1, shifted, b] - - # shift the mask into place - push.32 movup.3 # [b, 32, mask, shifted] - sub # [32 - b, mask, shifted] - u32shl # [mask << (32 - b), shifted] - u32or # [shifted | mask] - u32assert - else - u32shr - u32assert - end - end -end diff --git a/codegen/masm/intrinsics/mem.masm b/codegen/masm/intrinsics/mem.masm deleted file mode 100644 index 6b7e86831..000000000 --- a/codegen/masm/intrinsics/mem.masm +++ /dev/null @@ -1,333 +0,0 @@ -# Given an element index, and a word, in that order, drop the elements of the -# word other than the at the specified index. -# -# The element index must be in the range 0..=3. -export.extract_element # [element_index, w0, w1, w2, w3] - # assert the index given is valid - dup.0 push.3 lte assert - # compute a set of three booleans which used in conjunction with cdrop will - # extract the desired element of the given word - dup.0 push.3 lt movdn.5 # [element_index, w0, ..w3, element_index < 3] - dup.0 push.2 lt movdn.5 # [element_index, w0, ..w3, element_index < 2, ..] - push.1 lt # [element_index < 1, w0, ..w3, ..] - - # drop w1 if the element index is zero; or drop w0 if the element index is non-zero - cdrop - # drop w2 if the element index is one; or drop w0 and w1 if the element index is > 1 - movup.3 cdrop - # drop w3 if the element index is two; or drop w0, w1, and w2 if the element index is 3 - # - # after this point, the only value on the operand stack remaining will be - # the element of the word indicated by the index that was on the top of the - # stack on entry. We've consumed the word itself, as well as the element - # index - movup.2 cdrop -end - -# See `load_felt` for safe usage -proc.load_felt_unchecked # [waddr, index] - # prepare the stack to receive the loaded word - # [waddr, 0, 0, 0, 0, index] - padw movup.4 - # load the word which contains the desired element - mem_loadw # [w0, w1, w2, w3, index] - - # select the desired element - movup.4 - exec.extract_element -end - -# Load a field element from the given native pointer triplet. -# -# A native pointer triplet consists of a word address which contains the -# start of the data; an element index, which indicates which element of -# the word the data starts in; and a byte offset, which indicates which -# byte is the start of the data. -# -# A field element must be naturally aligned, i.e. it's byte offset must be zero. -export.load_felt # [waddr, index, offset] - # assert the pointer is felt-aligned, then load - movup.2 assertz exec.load_felt_unchecked -end - -# Load a single 32-bit machine word from the given native pointer triplet. -# -# A native pointer triplet consists of a word address which contains the -# start of the data; an element index, which indicates which element of -# the word the data starts in; and a byte offset, which indicates which -# byte is the start of the data. -export.load_sw # [waddr, index, offset] - # check for alignment and offset validity - dup.2 eq.0 - dup.3 push.8 u32lt assert # offset must be < 8 - # if the pointer is naturally aligned.. - if.true - # drop the byte offset - movup.2 drop - # load the element containing the data we want - exec.load_felt_unchecked - else - # check if the load starts in the first element - dup.1 eq.0 - if.true - # the load is across both the first and second elements - # drop the element index - swap.1 drop - # load - padw movup.4 mem_loadw # [w0, w1, w2, w3, offset] - # drop the unused elements - movup.3 movup.3 drop drop - # shift high bits left by the offset - dup.2 u32shl # [hi, w1, offset] - # move the low bits to the top and shift them as well - swap.1 push.32 movup.3 # [offset, 32, w1, hi] - u32overflowing_sub assertz # [32 - offset, w1, hi] - u32shr # [lo, hi] - # combine the two halves - u32or # [result] - else - # check if the load starts in the second element - dup.1 eq.1 - if.true - # the load is across both the second and third elements - # drop the element idnex - swap.1 drop - # load - padw movup.4 mem_loadw # [w0, w1, w2, w3, offset] - # drop the unused elements - drop movdn.2 movdn.2 drop # [w1, w2, offset] - # shift the high bits - dup.2 u32shl # [hi, w2, offset] - # shift the low bits - swap.1 push.32 movup.3 # [offset, 32, w2, hi] - u32overflowing_sub assertz # [32 - offset, w2, hi] - u32shr # [lo, hi] - # combine the two halves - u32or # [result] - else - # check if the load starts in the third element - swap.1 eq.2 - if.true - # the load is across both the third and fourth elements - padw movup.4 mem_loadw # [w0, w1, w2, w3, offset] - # drop first two unused - drop drop # [w2, w3, offset] - # shift the high bits - dup.2 u32shl # [hi, w3, offset] - # shift the low bits - swap.1 push.32 movup.3 # [offset, 32, w3, hi] - u32overflowing_sub assertz # [32 - offset, w3, hi] - u32shr # [lo, hi] - # combine the two halves - u32or # [result] - else - # the load crosses a word boundary - # start with the word containing the low bits - dup.0 # [waddr, waddr, offset] - u32overflowing_add.1 assertz # [waddr + 1, waddr, offset] - # load the word and drop the unused elements - padw movup.4 mem_loadw movdn.4 drop drop drop # [w0, waddr, offset] - # shift the low bits - push.32 dup.3 # [offset, 32, w0, waddr, offset] - u32overflowing_sub assertz # [32 - offset, w0, waddr, offset] - u32shr # [lo, waddr, offset] - # load the word with the high bits, drop unused elements - swap.1 padw movup.4 mem_loadw drop drop drop # [w3, lo, offset] - # shift high bits - movup.2 u32shl # [hi, lo] - # combine the two halves - u32or # [result] - end - end - end - end -end - -# This handles emitting code that handles aligning an unaligned double -# machine-word value which is split across three machine words (field elements). -# -# To recap: -# -# * A machine word is a 32-bit chunk stored in a single field element -# * A double word is a pair of 32-bit chunks -# * A quad word is a quartet of 32-bit chunks (i.e. a Miden "word") -# * An unaligned double-word requires three 32-bit chunks to represent, -# since the first chunk does not contain a full 32-bits, so an extra is -# needed to hold those bits. -# -# As an example, assume the pointer we are dereferencing is a u64 value, -# which has 8-byte alignment, and the value is stored 40 bytes from the -# nearest quad-word-aligned boundary. To load the value, we must fetch -# the full quad-word from the aligned address, drop the first word, as -# it is unused, and then recombine the 64 bits we need spread across -# the remaining three words to obtain the double-word value we actually want. -# -# The data, on the stack, is shown below: -# -# If we visualize which bytes are contained in each 32-bit chunk on the stack, -# we get: -# -# [0..=4, 5..=8, 9..=12] -# -# These byte indices are relative to the nearest word-aligned address, in the -# same order as they would occur in a byte-addressable address space. The -# significance of each byte depends on the value being dereferenced, but Miden -# is a little-endian machine, so typically the most significant bytes come first -# (i.e. also commonly referred to as "high" vs "low" bits). -# -# If we visualize the layout of the bits of our u64 value spread across the -# three chunks, we get: -# -# [00000000111111111111111111111111, 111111111111111111111111111111, 11111111111111111111111100000000] -# -# As illustrated above, what should be a double-word value is occupying three words. -# To "realign" the value, i.e. ensure that it is naturally aligned and fits in two -# words, we 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 -export.realign_dw # [chunk_hi, chunk_mid, chunk_lo, offset] - # We will refer to the parts of our desired double-word value - # as two parts, `x_hi` and `x_lo`. - # Re-align the high bits by shifting out the offset - # - # This gives us the first half of the first word. - dup.3 u32shl # [x_hi_hi, chunk_mid, chunk__lo, offset] - - # Move the value below the other chunks temporarily - movdn.2 # [chunk_mid, chunk_lo, x_hi_hi, offset] - - # 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: - # - dup.0 # [chunk_mid, chunk_mid, chunk_lo, offset, x_hi_hi] - - # Then, we shift the chunk right by 32 - offset bits, - # re-aligning the low bits of the first word, and - # isolating them. - push.32 dup.4 u32shr # [x_hi_lo, chunk_mid, chunk_lo, offset, x_hi_hi] - - # Move the high bits back to the top - # - # [x_hi_hi, x_hi_lo, chunk_mid, chunk_lo] - movup.4 # [x_hi_hi, x_hi_lo, chunk_mid, chunk_lo, offset] - - # OR the two parts of the `x_hi` chunk together - u32or # [x_hi, chunk_mid, chunk_lo, offset] - - # Move `x_hi` to the bottom for later - movdn.2 # [chunk_mid, chunk_lo, x_hi, offset] - - # 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] - dup.3 u32shl # [x_lo_hi, chunk_lo, x_hi, offset] - - # Next, swap the low bit chunk to the top temporarily - swap.1 - - # Shift the value right, as done previously for the middle chunk - push.32 movup.4 u32shr # [x_lo_lo, x_lo_hi, x_hi] - - # OR the two halves together, giving us our second word, `x_lo` - u32or # [x_lo, x_hi] - - # Swap the words so they are in the correct order - swap.1 # [x_hi, x_lo] -end - - -# Load a pair of machine words (32-bit elements) to the operand stack -export.load_dw # [waddr, index, offset] - # check for alignment and offset validity - dup.2 eq.0 - dup.3 push.8 u32lt assert # offset must be < 8 - # if the pointer is naturally aligned.. - if.true - # drop byte offset - movup.2 drop - # check which element to start at - dup.1 eq.0 - if.true - # drop index - swap.1 drop - # load first two elements - padw movup.4 mem_loadw - # drop last two elements, and we're done - movup.4 movup.4 drop drop - else - dup.1 eq.1 - if.true - # drop index - swap.1 drop - # load second and third elements - padw movup.4 mem_loadw - # drop unused elements, and we're done - drop movup.3 drop - else - swap.1 eq.2 - if.true - # load third and fourth elements, drop unused, and we're done - padw movup.4 mem_loadw drop drop - else - # load first element of next word, drop the rest - dup.0 u32overflowing_add.1 assertz padw movup.4 mem_loadw - movup.4 movup.4 movup.4 - drop drop drop - # load fourth element, and we're done - movup.4 padw movup.4 mem_loadw drop drop drop - end - end - end - else # unaligned; an unaligned double-word spans three elements - # check if we start in the first element - dup.1 eq.0 - if.true - # drop the index - swap.1 drop - # load three elements containing the double-word on the stack - padw movup.4 mem_loadw movup.4 drop - # re-align it, and we're done; realign_dw gets [w0, w1, w2, offset] - exec.realign_dw - else - # check if we start in the second element - dup.1 eq.1 - if.true - # drop the index - swap.1 drop - # load three elements containing the double-word on the stack - padw movup.4 mem_loadw drop - # re-align it, and we're done; realign_dw gets [w1, w2, w3, offset] - exec.realign_dw - else - # check if we start in the third element - swap.1 eq.2 - if.true - # load one element from the next word - dup.0 u32overflowing_add.1 assertz padw movup.4 mem_loadw - movup.4 movup.4 movup.4 drop drop drop - # load two elements from the first word - swap.1 padw movup.4 mem_loadw drop drop - # re-align it, and we're done; realign_dw gets [w2, w3, w0, offset] - exec.realign_dw - else - # load the two least-significant elements from the next word first - dup.0 u32overflowing_add.1 assertz padw movup.4 mem_loadw - movup.4 movup.4 drop drop - # load the most significant element from the first word - movup.2 padw movup.4 mem_loadw drop drop drop - # re-align it, and we're done; realign_dw gets [w3, w1, w0, offset] - exec.realign_dw - end - end - end - end -end diff --git a/codegen/masm/src/codegen/emit/binary.rs b/codegen/masm/src/codegen/emit/binary.rs deleted file mode 100644 index 220f22e9b..000000000 --- a/codegen/masm/src/codegen/emit/binary.rs +++ /dev/null @@ -1,1248 +0,0 @@ -use miden_hir::{assert_matches, Felt, Immediate, Overflow, Type}; - -use crate::masm::Op; - -use super::OpEmitter; - -impl<'a> OpEmitter<'a> { - pub fn eq(&mut self) { - 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 => { - self.eq_i128(); - } - Type::I64 | Type::U64 => { - // context: [b_hi, b_lo, a_hi, a_lo] - self.emit_all(&[ - // [a_hi, b_hi, b_lo, a_lo] - Op::Movup(2), - // [hi_equal, b_lo, a_lo] - Op::Eq, - // [b_lo, a_lo, hi_equal] - Op::Movdn(3), - // [lo_equal, hi_equal] - Op::Eq, - // [is_equal] - Op::And, - ]); - } - Type::Felt - | Type::Ptr(_) - | Type::U32 - | Type::I32 - | Type::U16 - | Type::I16 - | Type::I8 - | Type::U8 - | Type::I1 => { - self.emit(Op::Eq); - } - ty => unimplemented!("eq is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn eq_imm(&mut self, imm: Immediate) { - 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 => { - self.push_immediate(imm); - self.eq_i128(); - } - Type::I64 | Type::U64 => { - self.push_immediate(imm); - // context: [b_hi, b_lo, a_hi, a_lo] - self.emit_all(&[ - // [a_hi, b_hi, b_lo, a_lo] - Op::Movup(2), - // [hi_equal, b_lo, a_lo] - Op::Eq, - // [b_lo, a_lo, hi_equal] - Op::Movdn(3), - // [lo_equal, hi_equal] - Op::Eq, - // [is_equal] - Op::And, - ]); - } - Type::Felt | Type::Ptr(_) | Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::EqImm(imm.as_felt().unwrap())); - } - Type::I32 | Type::I16 | Type::I8 => { - self.emit(Op::EqImm(Felt::new(imm.as_i32().unwrap() as u32 as u64))); - } - ty => unimplemented!("eq is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn neq(&mut self) { - 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 => { - self.neq_i128(); - } - Type::I64 | Type::U64 => { - self.emit_all(&[ - // [a_hi, b_hi, b_lo, a_lo] - Op::Movup(2), - // [hi_not_equal, b_lo, a_lo] - Op::Neq, - // [b_lo, a_lo, hi_not_equal] - Op::Movdn(3), - // [lo_not_equal, hi_not_equal] - Op::Neq, - // [is_not_equal] - Op::Or, - ]); - } - Type::Felt - | Type::Ptr(_) - | Type::U32 - | Type::I32 - | Type::U16 - | Type::I16 - | Type::I8 - | Type::U8 - | Type::I1 => { - self.emit(Op::Neq); - } - ty => unimplemented!("neq is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn neq_imm(&mut self, imm: Immediate) { - 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 => { - self.push_immediate(imm); - self.neq_i128(); - } - Type::I64 | Type::U64 => { - self.push_immediate(imm); - self.emit_all(&[ - // [a_hi, b_hi, b_lo, a_lo] - Op::Movup(2), - // [hi_not_equal, b_lo, a_lo] - Op::Neq, - // [b_lo, a_lo, hi_not_equal] - Op::Movdn(3), - // [lo_not_equal, hi_not_equal] - Op::Neq, - // [is_not_equal] - Op::Or, - ]); - } - Type::Felt | Type::Ptr(_) | Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::NeqImm(imm.as_felt().unwrap())); - } - Type::I32 | Type::I16 | Type::I8 => { - self.emit(Op::NeqImm(Felt::new(imm.as_i32().unwrap() as u32 as u64))); - } - ty => unimplemented!("neq is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn gt(&mut self) { - 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); - } - Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::U32Gt); - } - Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_gt".parse().unwrap())), - ty => unimplemented!("gt is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn gt_imm(&mut self, imm: Immediate) { - 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())); - } - Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Gt]); - } - Type::I32 => { - self.emit_all(&[ - Op::PushU32(imm.as_i32().unwrap() as u32), - Op::Exec("intrinsics::i32::is_gt".parse().unwrap()), - ]); - } - ty => unimplemented!("gt is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn gte(&mut self) { - 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); - } - Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::U32Gte); - } - Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_gte".parse().unwrap())), - ty => unimplemented!("gte is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn gte_imm(&mut self, imm: Immediate) { - 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())); - } - Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Gte]); - } - Type::I32 => { - self.emit_all(&[ - Op::PushU32(imm.as_i32().unwrap() as u32), - Op::Exec("intrinsics::i32::is_gte".parse().unwrap()), - ]); - } - ty => unimplemented!("gte is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn lt(&mut self) { - 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); - } - Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::U32Lt); - } - Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_lt".parse().unwrap())), - ty => unimplemented!("lt is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn lt_imm(&mut self, imm: Immediate) { - 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())); - } - Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Lt]); - } - Type::I32 => { - self.emit_all(&[ - Op::PushU32(imm.as_i32().unwrap() as u32), - Op::Exec("intrinsics::i32::is_lt".parse().unwrap()), - ]); - } - ty => unimplemented!("lt is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn lte(&mut self) { - 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); - } - Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::U32Lte); - } - Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_lte".parse().unwrap())), - ty => unimplemented!("lte is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn lte_imm(&mut self, imm: Immediate) { - 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())); - } - Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Lte]); - } - Type::I32 => { - self.emit_all(&[ - Op::PushU32(imm.as_i32().unwrap() as u32), - Op::Exec("intrinsics::i32::is_lte".parse().unwrap()), - ]); - } - ty => unimplemented!("lte is not yet implemented for {ty}"), - } - self.push(Type::I1); - } - - pub fn add(&mut self, overflow: Overflow) { - 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); - } - Type::U64 => { - self.add_u64(overflow); - } - Type::U32 => { - self.add_u32(overflow); - } - Type::I32 => { - self.add_i32(overflow); - } - ty @ (Type::U16 | Type::U8 | Type::I1) => { - self.add_uint(ty.size_in_bits() as u32, overflow); - } - ty => unimplemented!("add is not yet implemented for {ty}"), - } - self.push(ty); - if overflow.is_overflowing() { - self.push(Type::I1); - } - } - - pub fn add_imm(&mut self, imm: Immediate, overflow: Overflow) { - 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 => { - self.emit(Op::AddImm(imm.as_felt().unwrap())); - } - Type::U64 => { - self.push_immediate(imm); - self.add_u64(overflow); - } - Type::U32 => { - self.add_imm_u32(imm.as_u32().unwrap(), overflow); - } - Type::I32 => { - self.add_imm_i32(imm.as_i32().unwrap(), overflow); - } - ty @ (Type::U16 | Type::U8 | Type::I1) => { - self.add_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow); - } - ty => unimplemented!("add is not yet implemented for {ty}"), - } - self.push(ty); - if overflow.is_overflowing() { - self.push(Type::I1); - } - } - - pub fn sub(&mut self, overflow: Overflow) { - 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); - } - Type::U64 => { - self.sub_u64(overflow); - } - Type::U32 => { - self.sub_u32(overflow); - } - Type::I32 => { - self.sub_i32(overflow); - } - ty @ (Type::U16 | Type::U8 | Type::I1) => { - self.sub_uint(ty.size_in_bits() as u32, overflow); - } - ty => unimplemented!("sub is not yet implemented for {ty}"), - } - self.push(ty); - if overflow.is_overflowing() { - self.push(Type::I1); - } - } - - pub fn sub_imm(&mut self, imm: Immediate, overflow: Overflow) { - 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())); - } - Type::U64 => { - self.push_immediate(imm); - self.sub_u64(overflow); - } - Type::U32 => { - self.sub_imm_u32(imm.as_u32().unwrap(), overflow); - } - Type::I32 => { - self.sub_imm_i32(imm.as_i32().unwrap(), overflow); - } - ty @ (Type::U16 | Type::U8 | Type::I1) => { - self.sub_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow); - } - ty => unimplemented!("sub is not yet implemented for {ty}"), - } - self.push(ty); - if overflow.is_overflowing() { - self.push(Type::I1); - } - } - - pub fn mul(&mut self, overflow: Overflow) { - 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 mul operands to be the same type"); - match &ty { - Type::I128 => { - // We can use the Karatsuba algorithm for multiplication here: - // - // x = x_hi * 2^63 + x_lo - // y = y_hi * 2^63 + x_lo - // - // z2 = x_hi * y_hi - // z0 = x_lo * y_lo - // z1 = (x_hi + x_lo) * (y_hi + y_lo) - z2 - z0 - // - // z = z2 * (2^63)^2 + z1 * 2^63 + z0 - // - // We assume the stack holds two words representing x and y, with y on top of the stack - todo!() - } - Type::U64 => self.mul_u64(overflow), - Type::Felt => { - assert_matches!( - overflow, - Overflow::Unchecked | Overflow::Wrapping, - "only unchecked or wrapping semantics are supported for felt" - ); - self.emit(Op::Mul); - } - Type::U32 => self.mul_u32(overflow), - Type::I32 => self.mul_i32(overflow), - ty @ (Type::U16 | Type::U8) => { - self.mul_uint(ty.size_in_bits() as u32, overflow); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: mul expects integer operands, got {ty}") - } - ty => unimplemented!("mul for {ty} is not supported"), - } - self.push(ty); - if overflow.is_overflowing() { - self.push(Type::I1); - } - } - - pub fn mul_imm(&mut self, imm: Immediate, overflow: Overflow) { - 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"); - match &ty { - Type::U64 => { - self.push_immediate(imm); - self.mul_u64(overflow); - } - Type::Felt => { - assert_matches!( - overflow, - Overflow::Unchecked | Overflow::Wrapping, - "only unchecked or wrapping semantics are supported for felt" - ); - self.emit(Op::MulImm(imm.as_felt().unwrap())); - } - Type::U32 => self.mul_imm_u32(imm.as_u32().unwrap(), overflow), - Type::I32 => self.mul_imm_i32(imm.as_i32().unwrap(), overflow), - ty @ (Type::U16 | Type::U8) => { - self.mul_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: mul expects integer operands, got {ty}") - } - ty => unimplemented!("mul for {ty} is not supported"), - } - self.push(ty); - if overflow.is_overflowing() { - self.push(Type::I1); - } - } - - pub fn checked_div(&mut self) { - 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::U64 => self.checked_div_u64(), - Type::Felt => { - self.emit(Op::Div); - } - Type::U32 => self.checked_div_u32(), - Type::I32 => self.checked_div_i32(), - ty @ (Type::U16 | Type::U8) => { - self.checked_div_uint(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: div expects integer operands, got {ty}") - } - ty => unimplemented!("div for {ty} is not supported"), - } - self.push(ty); - } - - pub fn checked_div_imm(&mut self, imm: Immediate) { - 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::U64 => { - assert_ne!(imm.as_u64().unwrap(), 0, "invalid division by zero"); - self.push_immediate(imm); - self.checked_div_u64(); - } - Type::Felt => { - self.emit(Op::Div); - } - Type::U32 => self.checked_div_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.checked_div_imm_i32(imm.as_i32().unwrap()), - ty @ (Type::U16 | Type::U8) => { - self.checked_div_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: div expects integer operands, got {ty}") - } - ty => unimplemented!("div for {ty} is not supported"), - } - self.push(ty); - } - - pub fn unchecked_div(&mut self) { - 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::U64 => self.unchecked_div_u64(), - Type::Felt => { - self.emit(Op::Div); - } - Type::U32 | Type::U16 | Type::U8 => self.unchecked_div_u32(), - Type::I32 => self.checked_div_i32(), - ty if !ty.is_integer() => { - panic!("invalid binary operand: div expects integer operands, got {ty}") - } - ty => unimplemented!("div for {ty} is not supported"), - } - self.push(ty); - } - - pub fn unchecked_div_imm(&mut self, imm: Immediate) { - 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::U64 => { - assert_ne!(imm.as_u64().unwrap(), 0, "invalid division by zero"); - self.push_immediate(imm); - self.unchecked_div_u64(); - } - Type::Felt => { - self.emit(Op::Div); - } - Type::U32 => self.unchecked_div_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.checked_div_imm_i32(imm.as_i32().unwrap()), - ty @ (Type::U16 | Type::U8) => { - self.unchecked_div_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: div expects integer operands, got {ty}") - } - ty => unimplemented!("div for {ty} is not supported"), - } - self.push(ty); - } - - pub fn checked_mod(&mut self) { - 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(), - ty @ (Type::U16 | Type::U8) => { - self.checked_mod_uint(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: mod expects integer operands, got {ty}") - } - ty => unimplemented!("mod for {ty} is not supported"), - } - self.push(ty); - } - - pub fn checked_mod_imm(&mut self, imm: Immediate) { - 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(); - } - Type::U32 => self.checked_mod_imm_u32(imm.as_u32().unwrap()), - ty @ (Type::U16 | Type::U8) => { - self.checked_mod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: mod expects integer operands, got {ty}") - } - ty => unimplemented!("mod for {ty} is not supported"), - } - self.push(ty); - } - - pub fn unchecked_mod(&mut self) { - 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(), - ty @ (Type::U16 | Type::U8) => { - self.unchecked_mod_uint(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: mod expects integer operands, got {ty}") - } - ty => unimplemented!("mod for {ty} is not supported"), - } - self.push(ty); - } - - pub fn unchecked_mod_imm(&mut self, imm: Immediate) { - 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(); - } - Type::U32 => self.unchecked_mod_imm_u32(imm.as_u32().unwrap()), - ty @ (Type::U16 | Type::U8) => { - self.unchecked_mod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: mod expects integer operands, got {ty}") - } - ty => unimplemented!("mod for {ty} is not supported"), - } - self.push(ty); - } - - pub fn checked_divmod(&mut self) { - 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(), - ty @ (Type::U16 | Type::U8) => { - self.checked_divmod_uint(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: divmod expects integer operands, got {ty}") - } - ty => unimplemented!("divmod for {ty} is not supported"), - } - self.push(ty.clone()); - self.push(ty); - } - - pub fn checked_divmod_imm(&mut self, imm: Immediate) { - 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(); - } - Type::U32 => self.checked_divmod_imm_u32(imm.as_u32().unwrap()), - ty @ (Type::U16 | Type::U8) => { - self.checked_divmod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: divmod expects integer operands, got {ty}") - } - ty => unimplemented!("divmod for {ty} is not supported"), - } - self.push(ty.clone()); - self.push(ty); - } - - pub fn unchecked_divmod(&mut self) { - 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(), - ty @ (Type::U16 | Type::U8) => { - self.unchecked_divmod_uint(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: divmod expects integer operands, got {ty}") - } - ty => unimplemented!("divmod for {ty} is not supported"), - } - self.push(ty.clone()); - self.push(ty); - } - - pub fn unchecked_divmod_imm(&mut self, imm: Immediate) { - 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(); - } - Type::U32 => self.unchecked_divmod_imm_u32(imm.as_u32().unwrap()), - ty @ (Type::U16 | Type::U8) => { - self.unchecked_divmod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: divmod expects integer operands, got {ty}") - } - ty => unimplemented!("divmod for {ty} is not supported"), - } - self.push(ty.clone()); - self.push(ty); - } - - pub fn exp(&mut self) { - 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 exp operands to be the same type"); - match &ty { - Type::U64 => todo!("exponentiation by squaring"), - Type::Felt => { - self.emit(Op::Exp); - } - Type::U32 => { - self.emit_all(&[Op::Exp, Op::U32Assert]); - } - Type::I32 => { - self.emit(Op::Exec("intrinsics::i32::ipow".parse().unwrap())); - } - ty @ (Type::U16 | Type::U8) => { - self.emit_all(&[Op::Exp, Op::U32Assert]); - self.int32_to_uint(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: exp expects integer operands, got {ty}") - } - ty => unimplemented!("mod for {ty} is not supported"), - } - self.push(ty); - } - - pub fn exp_imm(&mut self, imm: Immediate) { - 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"); - let exp: u8 = imm - .as_u64() - .unwrap() - .try_into() - .expect("invalid exponent: must be value < 64"); - match &ty { - Type::U64 => todo!("exponentiation by squaring"), - Type::Felt => { - self.emit(Op::ExpImm(exp)); - } - Type::U32 => { - self.emit_all(&[Op::ExpImm(exp), Op::U32Assert]); - } - Type::I32 => { - self.emit_all(&[ - Op::PushU8(exp), - Op::Exec("intrinsics::i32::ipow".parse().unwrap()), - ]); - } - ty @ (Type::U16 | Type::U8) => { - self.emit_all(&[Op::ExpImm(exp), Op::U32Assert]); - self.int32_to_uint(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: exp expects integer operands, got {ty}") - } - ty => unimplemented!("mod for {ty} is not supported"), - } - self.push(ty); - } - - pub fn and(&mut self) { - 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.push(ty); - } - - pub fn and_imm(&mut self, imm: Immediate) { - 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.push(ty); - } - - pub fn or(&mut self) { - 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.push(ty); - } - - pub fn or_imm(&mut self, imm: Immediate) { - 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.push(ty); - } - - pub fn xor(&mut self) { - 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.push(ty); - } - - pub fn xor_imm(&mut self, imm: Immediate) { - 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.push(ty); - } - - pub fn band(&mut self) { - 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 band operands to be the same type"); - match &ty { - 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), - ty if !ty.is_integer() => { - panic!("invalid binary operand: band expects integer operands, got {ty}") - } - ty => unimplemented!("band for {ty} is not supported"), - } - self.push(ty); - } - - pub fn band_imm(&mut self, imm: Immediate) { - 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::U64 | Type::I64 => { - self.push_immediate(imm); - self.band_int64(); - } - Type::U32 | Type::U16 | Type::U8 => self.band_imm_u32(imm.as_u32().unwrap()), - Type::I32 | Type::I16 | Type::I8 => { - self.band_imm_u32(imm.as_i64().unwrap() as u64 as u32) - } - Type::I1 => self.emit(Op::AndImm(imm.as_bool().unwrap())), - ty if !ty.is_integer() => { - panic!("invalid binary operand: band expects integer operands, got {ty}") - } - ty => unimplemented!("band for {ty} is not supported"), - } - self.push(ty); - } - - pub fn bor(&mut self) { - 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 bor operands to be the same type"); - match &ty { - 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), - ty if !ty.is_integer() => { - panic!("invalid binary operand: bor expects integer operands, got {ty}") - } - ty => unimplemented!("bor for {ty} is not supported"), - } - self.push(ty); - } - - pub fn bor_imm(&mut self, imm: Immediate) { - 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::U64 | Type::I64 => { - self.push_immediate(imm); - self.bor_int64(); - } - Type::U32 | Type::U16 | Type::U8 => self.bor_imm_u32(imm.as_u32().unwrap()), - Type::I32 | Type::I16 | Type::I8 => { - self.bor_imm_u32(imm.as_i64().unwrap() as u64 as u32) - } - Type::I1 => self.emit(Op::AndImm(imm.as_bool().unwrap())), - ty if !ty.is_integer() => { - panic!("invalid binary operand: bor expects integer operands, got {ty}") - } - ty => unimplemented!("bor for {ty} is not supported"), - } - self.push(ty); - } - - pub fn bxor(&mut self) { - 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 bxor operands to be the same type"); - match &ty { - Type::U64 | Type::I64 => self.bxor_int64(), - Type::U32 | Type::I32 => self.bxor_u32(), - ty @ (Type::U16 | Type::I16 | Type::U8 | Type::I8) => { - self.bxor_u32(); - self.trunc_int32(ty.size_in_bits() as u32); - } - Type::I1 => self.emit(Op::Xor), - ty if !ty.is_integer() => { - panic!("invalid binary operand: bxor expects integer operands, got {ty}") - } - ty => unimplemented!("bxor for {ty} is not supported"), - } - self.push(ty); - } - - pub fn bxor_imm(&mut self, imm: Immediate) { - 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::U64 | Type::I64 => { - self.push_immediate(imm); - self.bxor_int64(); - } - Type::U32 => self.bxor_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.bxor_imm_u32(imm.as_i64().unwrap() as u64 as u32), - ty @ (Type::U16 | Type::U8) => { - self.bxor_imm_u32(imm.as_u32().unwrap()); - self.trunc_int32(ty.size_in_bits() as u32); - } - 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); - } - Type::I1 => self.emit(Op::XorImm(imm.as_bool().unwrap())), - ty if !ty.is_integer() => { - panic!("invalid binary operand: bxor expects integer operands, got {ty}") - } - ty => unimplemented!("bxor for {ty} is not supported"), - } - self.push(ty); - } - - pub fn shl(&mut self) { - 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 shl operands to be the same type"); - match &ty { - Type::U64 => self.shl_u64(), - Type::U32 | Type::I32 => self.shl_u32(), - ty @ (Type::U16 | Type::U8) => { - self.shl_u32(); - self.trunc_int32(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: shl expects integer operands, got {ty}") - } - ty => unimplemented!("shl for {ty} is not supported"), - } - self.push(ty); - } - - pub fn shl_imm(&mut self, imm: Immediate) { - let lhs = self.pop().expect("operand stack is empty"); - let ty = lhs.ty(); - assert_eq!(ty, imm.ty(), "expected shl operands to be the same type"); - match &ty { - Type::U64 => { - assert!( - imm.as_u64().unwrap() < 64, - "invalid shift value: must be < 64" - ); - self.push_immediate(imm); - self.shl_u64(); - } - Type::U32 => self.shl_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.shl_imm_u32(imm.as_i32().unwrap() as u32), - ty @ (Type::U16 | Type::U8) => { - self.shl_imm_u32(imm.as_u32().unwrap()); - self.trunc_int32(ty.size_in_bits() as u32); - } - ty if !ty.is_integer() => { - panic!("invalid binary operand: shl expects integer operands, got {ty}") - } - ty => unimplemented!("shl for {ty} is not supported"), - } - self.push(ty); - } - - pub fn shr(&mut self) { - 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 shr operands to be the same type"); - match &ty { - Type::U64 => self.shr_u64(), - Type::U32 | Type::U16 | Type::U8 => self.shr_u32(), - Type::I32 => self.shr_i32(), - ty if !ty.is_integer() => { - panic!("invalid binary operand: shr expects integer operands, got {ty}") - } - ty => unimplemented!("shr for {ty} is not supported"), - } - self.push(ty); - } - - pub fn shr_imm(&mut self, imm: Immediate) { - let lhs = self.pop().expect("operand stack is empty"); - let ty = lhs.ty(); - assert_eq!(ty, imm.ty(), "expected shr operands to be the same type"); - match &ty { - Type::U64 => { - let shift = imm.as_u64().unwrap(); - assert!(shift < 64, "invalid shift value: must be < 64, got {shift}"); - self.push_immediate(imm); - self.shr_u64(); - } - Type::U32 | Type::U16 | Type::U8 => self.shr_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.shr_imm_i32(imm.as_i32().unwrap()), - ty if !ty.is_integer() => { - panic!("invalid binary operand: shr expects integer operands, got {ty}") - } - ty => unimplemented!("shr for {ty} is not supported"), - } - self.push(ty); - } - - pub fn rotl(&mut self) { - 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 rotl operands to be the same type"); - match &ty { - Type::U64 => self.rotl_u64(), - Type::U32 => self.rotl_u32(), - ty if !ty.is_integer() => { - panic!("invalid binary operand: rotl expects integer operands, got {ty}") - } - ty => unimplemented!("rotl for {ty} is not supported"), - } - self.push(ty); - } - - pub fn rotl_imm(&mut self, imm: Immediate) { - let lhs = self.pop().expect("operand stack is empty"); - let ty = lhs.ty(); - assert_eq!(ty, imm.ty(), "expected rotl operands to be the same type"); - match &ty { - Type::U64 => { - self.push_immediate(imm); - self.rotl_u64(); - } - Type::U32 => self.rotl_imm_u32(imm.as_u32().unwrap()), - ty if !ty.is_integer() => { - panic!("invalid binary operand: rotl expects integer operands, got {ty}") - } - ty => unimplemented!("rotl for {ty} is not supported"), - } - self.push(ty); - } - - pub fn rotr(&mut self) { - 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 rotr operands to be the same type"); - match &ty { - Type::U64 => self.rotr_u64(), - Type::U32 => self.rotr_u32(), - ty if !ty.is_integer() => { - panic!("invalid binary operand: rotr expects integer operands, got {ty}") - } - ty => unimplemented!("rotr for {ty} is not supported"), - } - self.push(ty); - } - - pub fn rotr_imm(&mut self, imm: Immediate) { - let lhs = self.pop().expect("operand stack is empty"); - let ty = lhs.ty(); - assert_eq!(ty, imm.ty(), "expected rotr operands to be the same type"); - match &ty { - Type::U64 => { - self.push_immediate(imm); - self.rotr_u64(); - } - Type::U32 => self.rotr_imm_u32(imm.as_u32().unwrap()), - ty if !ty.is_integer() => { - panic!("invalid binary operand: rotr expects integer operands, got {ty}") - } - ty => unimplemented!("rotr for {ty} is not supported"), - } - self.push(ty); - } - - pub fn min(&mut self) { - 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::U32 | Type::U16 | Type::U8 | Type::I1 => self.min_u32(), - Type::I32 => self.min_i32(), - ty if !ty.is_integer() => { - panic!("invalid binary operand: min expects integer operands, got {ty}") - } - ty => unimplemented!("min for {ty} is not supported"), - } - self.push(ty); - } - - pub fn min_imm(&mut self, imm: Immediate) { - 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(); - } - 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()), - ty if !ty.is_integer() => { - panic!("invalid binary operand: min expects integer operands, got {ty}") - } - ty => unimplemented!("min for {ty} is not supported"), - } - self.push(ty); - } - - pub fn max(&mut self) { - 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::U32 | Type::U16 | Type::U8 | Type::I1 => self.max_u32(), - Type::I32 => self.max_i32(), - ty if !ty.is_integer() => { - panic!("invalid binary operand: max expects integer operands, got {ty}") - } - ty => unimplemented!("max for {ty} is not supported"), - } - self.push(ty); - } - - pub fn max_imm(&mut self, imm: Immediate) { - 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(); - } - 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()), - ty if !ty.is_integer() => { - panic!("invalid binary operand: max expects integer operands, got {ty}") - } - ty => unimplemented!("max for {ty} is not supported"), - } - self.push(ty); - } -} diff --git a/codegen/masm/src/codegen/emit/felt.rs b/codegen/masm/src/codegen/emit/felt.rs deleted file mode 100644 index e2f8ab66b..000000000 --- a/codegen/masm/src/codegen/emit/felt.rs +++ /dev/null @@ -1,174 +0,0 @@ -use miden_hir::{Felt, FieldElement}; - -use crate::masm::Op; - -use super::OpEmitter; - -/// The value zero, as a field element -pub const ZERO: Felt = Felt::ZERO; - -/// The value 2^32, as a field element -pub const U32_FIELD_MODULUS: Felt = Felt::new(2u64.pow(32)); - -#[allow(unused)] -impl<'a> OpEmitter<'a> { - /// This operation checks if the field element on top of the stack is zero. - /// - /// This operation does not consume the input, and pushes a boolean value on the stack. - /// - /// # Stack effects - /// - /// `[a, ..] => [a == 0, a, ..]` - #[inline(always)] - pub fn felt_is_zero(&mut self) { - self.emit_all(&[Op::Dup(0), Op::EqImm(ZERO)]); - } - - /// This operation asserts the field element on top of the stack is zero. - /// - /// This operation does not consume the input. - /// - /// # Stack effects - /// - /// `[a, ..] => [a, ..]` - #[inline(always)] - pub fn assert_felt_is_zero(&mut self) { - self.emit_all(&[Op::Dup(0), Op::Assertz]); - } - - /// Convert a field element to i128 by zero-extension. - /// - /// This consumes the field element on top of the stack. - /// - /// # Stack effects - /// - /// `[a, ..] => [0, 0, a_hi, a_lo]` - #[inline] - pub fn felt_to_i128(&mut self) { - self.emit_all(&[Op::U32Split, Op::Push2([ZERO, ZERO])]); - } - - /// Convert a field element to u64 by zero-extension. - /// - /// This consumes the field element on top of the stack. - /// - /// # Stack effects - /// - /// `[a, ..] => [a_hi, a_lo]` - #[inline(always)] - pub fn felt_to_u64(&mut self) { - self.emit(Op::U32Split); - } - - /// Convert a field element to i64 by zero-extension. - /// - /// Asserts if the field element is too large to represent as an i64. - /// - /// This consumes the field element on top of the stack. - /// - /// # Stack effects - /// - /// `[a, ..] => [a_hi, a_lo]` - #[inline(always)] - pub fn felt_to_i64(&mut self) { - self.felt_to_u64(); - self.assert_unsigned_int64(); - } - - /// 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) { - assert_valid_integer_size!(n, 1, 32); - self.emit_all(&[ - // Split into u32 limbs - Op::U32Split, - // Assert most significant 32 bits are unused - Op::EqImm(ZERO), - Op::Assert, - ]); - if n < 32 { - // Convert to N-bit integer - self.int32_to_uint(n); - } - } - - /// 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) { - assert_valid_integer_size!(n, 1, 32); - self.emit_all(&[ - // Split into u32 limbs - Op::U32Split, - // Assert most significant 32 bits are unused - Op::EqImm(ZERO), - Op::Assert, - ]); - // Assert the sign bit isn't set - self.assert_unsigned_int32(); - if n < 32 { - // Convert to signed N-bit integer - self.int32_to_int(n); - } - } - - /// 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) { - assert_valid_integer_size!(n, 64, 256); - match n { - 64 => self.felt_to_u64(), - 128 => self.felt_to_i128(), - n => { - // Convert to u64 and zero-extend - self.felt_to_u64(); - self.zext_int64(n); - } - } - } - - /// Emits code to sign-extend a field element value to an N-bit integer, where N >= 64 - /// - /// Field elements are unsigned, so sign-extension here is indicating that the target - /// 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) { - assert_valid_integer_size!(n, 64, 256); - match n { - 64 => self.felt_to_i64(), - 128 => self.felt_to_i128(), - n => { - // Convert to i64 and sign-extend - self.felt_to_i64(); - self.sext_int64(n); - } - } - } - - /// Truncates a field element on top of the stack to an N-bit integer, where N <= 32. - /// - /// Truncation on field elements is not well-defined, because field elements do not have - /// a specified bitwise representation. To implement semantics equivalent to the other types - /// which _do_ have a specified representation, we first convert the input field element to u32, - /// and then masking out any additional unused bits of the u32 representation. - /// - /// 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) { - // 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); - } - - /// 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)); - } -} diff --git a/codegen/masm/src/codegen/emit/int128.rs b/codegen/masm/src/codegen/emit/int128.rs deleted file mode 100644 index 551c54d5e..000000000 --- a/codegen/masm/src/codegen/emit/int128.rs +++ /dev/null @@ -1,175 +0,0 @@ -use crate::masm::Op; - -use super::OpEmitter; - -#[allow(unused)] -impl<'a> OpEmitter<'a> { - /// Checks if the i128 value on the stack has its sign bit set. - #[inline(always)] - pub fn is_signed_i128(&mut self) { - self.is_signed_int32() - } - - /// Assert that the i128 value on the stack does not have its sign bit set. - #[inline(always)] - pub fn assert_unsigned_i128(&mut self) { - // Assert that the sign bit is unset - self.assert_unsigned_int32() - } - - /// 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) { - 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], - ]); - 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); - } - - /// Convert an i128 value to a field element value. - /// - /// This is different than `trunc_i128_to_felt`, as this function performs a - /// range check on the input value to ensure that it will fit in a felt. - /// - /// This consumes the input value, and leaves a felt value on the stack. - /// Execution traps if the input value cannot fit in a field element. - /// - /// 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_felt(&mut self) { - // First, convert to u64 - self.i128_to_u64(); - // Then convert the u64 to felt - self.u64_to_felt(); - } - - /// Convert an i128 value to u64 - /// - /// This is different than `trunc_i128_to_u64`, as this function performs a - /// range check on the input value to ensure that it will fit in a u64. - /// - /// This consumes the input value, and leaves a u64 value on the stack. - /// - /// 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_u64(&mut self) { - // Assert the first element is equal to 0 without consuming it - // - // This has the effect of validating not only that the value is - // unsigned, but that the value is small enough to fit in an i96 - self.assert_felt_is_zero(); - // Assert that the high 64 bits are all zero - // - // This extends the range check above, validating that the input - // is small enough to fit in an i64 - // - // What remains on the stack at this point are the low 64-bits, - // which is also our result. - self.emit(Op::AssertEq); - } - - /// Convert an i128 value to i64 - /// - /// This is different than `trunc_i128_to_i64`, as this function performs a - /// range check on the input value to ensure that it will fit in a i64. - /// - /// This consumes the input value, and leaves an i64 value on the stack. - /// - /// 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) { - // Determine if this value is signed or not - self.is_signed_int32(); - // Preserving the is_signed flag, select the expected hi bits value - self.emit(Op::Dup(0)); - self.select_int32(u32::MAX, 0); - // Move the most significant 64 bits to top of stack - self.move_int64_up(2); - // Move expected value to top of stack - self.emit(Op::Movup(2)); - // 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), - ]); - // [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, - ]); - } - - /// Truncate this i128 value to a felt value - /// - /// This consumes the input value, and leaves a felt value on the stack. - /// - /// 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(); - } - - /// Truncate this i128 value to N bits, where N is <= 64 - /// - /// This consumes the input value, and leaves an N-bit value on the stack, - /// where the the value is assumed to be represented using 32-bit limbs. - /// For example, a 64-bit value will consist of two 32-bit values on the - /// stack. - /// - /// 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) { - assert_valid_integer_size!(n, 1, 64); - match n { - 64 => { - self.emit_n(2, Op::Drop); - } - 32 => { - self.emit_n(3, Op::Drop); - } - n => { - self.trunc_int32(n); - } - } - } - - /// 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, - ]); - } - - /// 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); - } -} diff --git a/codegen/masm/src/codegen/emit/int32.rs b/codegen/masm/src/codegen/emit/int32.rs deleted file mode 100644 index e5aaa910e..000000000 --- a/codegen/masm/src/codegen/emit/int32.rs +++ /dev/null @@ -1,910 +0,0 @@ -use miden_hir::{Felt, FieldElement, Overflow}; - -use crate::masm::Op; - -use super::OpEmitter; - -pub const SIGN_BIT: u32 = 1 << 31; - -#[allow(unused)] -impl<'a> OpEmitter<'a> { - /// Emits code to apply a constant 32-bit mask, `mask`, to a u32 value on top of the stack. - /// - /// The value on top of the stack IS consumed. - /// - /// NOTE: This function does not validate that the value on top of the stack is - /// a valid u32 - the caller is responsible for such validation. - /// - /// # Stack Effects - /// - /// `[a, ..] => [a & mask, ..]` - #[inline] - pub fn const_mask_u32(&mut self, mask: u32) { - self.emit_all(&[Op::PushU32(mask), Op::U32And]); - } - - /// Emits code to apply a 32-bit mask, `mask`, to a u32 value, `input`. - /// - /// Both `mask` and `input` are operands on the stack, with `mask` on top. - /// - /// While `mask` is consumed by this operation, `input` IS NOT consumed. - /// - /// NOTE: This function assumes that the caller has validated that both values are valid u32. - /// - /// # Stack Effects - /// - /// `[mask, input, ..] => [input & mask, input]` - #[inline] - pub fn mask_u32(&mut self) { - self.emit_all(&[Op::Dup(1), Op::U32And]); - } - - /// Emits code to check if all bits of `flags` are set in the u32 value on top of the stack. - /// - /// The value on top of the stack IS NOT consumed. - /// - /// NOTE: This function does not validate that the value on top of the stack is - /// a valid u32 - the caller is responsible for such validation. - /// - /// # Stack Effects - /// - /// `[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))); - } - - /// Emits code to check if all bits of `mask` are set in `input`. - /// - /// Both `mask` and `input` are operands on the stack, with `mask` on top. - /// - /// While `mask` is consumed by this operation, `input` IS NOT consumed. - /// - /// NOTE: This function assumes that the caller has validated that both values are valid u32. - /// - /// # Stack Effects - /// - /// `[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] - ]); - } - - /// Check if a 32-bit integer value on the operand stack has its sign bit set. - /// - /// The value on top of the stack IS NOT consumed. - /// - /// 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); - } - - /// 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); - } - - /// Emits code to assert that a 32-bit value on the operand stack has the i32 sign bit set. - /// - /// The value on top of the stack IS NOT consumed. - /// - /// 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); - } - - /// Emits code to assert that a 32-bit value on the operand stack does not have the i32 sign bit set. - /// - /// The value on top of the stack IS NOT consumed. - /// - /// 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); - } - - /// Emits code to assert that a 32-bit value on the operand stack is equal to the given constant value. - /// - /// The value on top of the stack IS NOT consumed. - /// - /// # Stack Effects - /// - /// `[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]); - } - - /// Emits code to assert that two 32-bit values, `expected` and `value`, on top of the operand stack are equal, - /// without consuming `value`. - /// - /// The `expected` operand is consumed, while the `value` operand IS NOT. - /// - /// # Stack Effects - /// - /// `[expected, input, ..] => [input, ..]` - #[inline] - pub fn assert_eq_u32(&mut self) { - self.emit_all(&[Op::Dup(1), Op::AssertEq]); - } - - /// Emits code to select a constant u32 value, using the `n`th value on the operand - /// stack as the condition for the select. - /// - /// This function pushes `b` then `a` on the stack, moves the `n`th value to the top - /// of the stack, and then executes a conditional drop. This has the effect of consuming - /// 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) { - 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); - } else { - self.emit_all(&[Op::PushU32(b), Op::PushU32(a), Op::Movup(n + 2), Op::Cdrop]); - } - } - - /// Same semantics as `mov_select`, but copies the `n`th value on the operand - /// stack rather than moving it. - /// - /// # Stack Effects - /// - /// 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) { - 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); - } else { - self.emit_all(&[Op::PushU32(b), Op::PushU32(a), Op::Dup(n + 2), Op::Cdrop]); - } - } - - /// Emits code to select between two u32 constants, given a boolean value on top of the stack - /// - /// # 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]); - } - - /// 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) { - assert_valid_integer_size!(n, 1, 32); - // Push is_signed on the stack - self.is_signed_int32(); - // 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, - ]); - } - - /// 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) { - assert_valid_integer_size!(n, 1, 32); - // Push is_signed on the stack - self.is_signed_int32(); - // 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, - ]); - } - - /// 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) { - 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, - ]); - } - - /// 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) { - 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), - ]); - } - - /// Emit code to truncate a 32-bit value on top of the operand stack, to N bits, where N is <= 32 - /// - /// This consumes the input value, and leaves an N-bit value on the stack. - /// - /// 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) { - 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)); - } - } - - /// Emit code to zero-extend a 32-bit value to N bits, where N <= 128 - /// - /// This operation assumes all N-bit integers greater than 32 bits use 32-bit limbs. - /// - /// 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) { - assert_valid_integer_size!(n, 32); - // Only values larger than 32 bits require padding - if n <= 32 { - return; - } - 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)); - } - - /// Emit code to sign-extend a signed 32-bit value to N bits, where N <= 128 - /// - /// This operation assumes all N-bit integers greater than 32 bits use 32-bit limbs. - /// - /// NOTE: This operation treats the most significant bit as the sign bit, it is - /// 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) { - assert_valid_integer_size!(n, 32); - self.is_signed_int32(); - self.select_int32(u32::MAX, 0); - self.pad_int32(n); - } - - /// Emit code to pad a 32-bit value out to N bits, where N >= 32. - /// - /// N must be a power of two. - /// - /// The padding value is expected on top of the stack, followed by the 32-bit value to pad. - /// - /// This operation assumes all N-bit integers greater than 32 bits use 32-bit limbs. - /// - /// 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) { - 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)); - } - - /// Push a u32 value on the stack - #[inline(always)] - pub fn push_u32(&mut self, i: u32) { - self.emit(Op::PushU32(i)); - } - - /// Push a i32 value on the stack - #[inline(always)] - pub fn push_i32(&mut self, i: i32) { - self.emit(Op::PushU32(i as u32)); - } - - /// 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, - }); - } - - /// 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()), - }) - } - - /// Pops a u32 value off the stack, `a`, and performs `a + `. - /// - /// See the [Overflow] type for how overflow semantics can change the operation. - /// - /// Adding zero is a no-op. - #[inline] - pub fn add_imm_u32(&mut self, imm: u32, overflow: Overflow) { - 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), - }); - } - - /// Pops a i32 value off the stack, `a`, and performs `a + `. - /// - /// See the [Overflow] type for how overflow semantics can change the operation. - /// - /// Adding zero is a no-op. - #[inline] - pub fn add_imm_i32(&mut self, imm: i32, overflow: Overflow) { - if imm == 0 { - return; - } - match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.add_imm_u32(imm as u32, overflow), - Overflow::Checked => { - self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::checked_add".parse().unwrap()), - ]); - } - Overflow::Overflowing => self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::overflowing_add".parse().unwrap()), - ]), - } - } - - /// 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, - }); - } - - /// 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) { - match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.sub_u32(overflow), - Overflow::Checked => { - self.emit(Op::Exec("intrinsics::i32::checked_sub".parse().unwrap())) - } - Overflow::Overflowing => self.emit(Op::Exec( - "intrinsics::i32::overflowing_sub".parse().unwrap(), - )), - } - } - - /// Pops a u32 value off the stack, `a`, and performs `a - `. - /// - /// See the [Overflow] type for how overflow semantics can change the operation. - /// - /// Subtracting zero is a no-op. - #[inline] - pub fn sub_imm_u32(&mut self, imm: u32, overflow: Overflow) { - 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), - }); - } - - /// Pops a i32 value off the stack, `a`, and performs `a - `. - /// - /// See the [Overflow] type for how overflow semantics can change the operation. - /// - /// Subtracting zero is a no-op. - #[inline] - pub fn sub_imm_i32(&mut self, imm: i32, overflow: Overflow) { - 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()), - ]), - } - } - - /// 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, - }); - } - - /// 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) { - match overflow { - Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("intrinsics::i32::wrapping_mul".parse().unwrap())) - } - Overflow::Checked => { - self.emit(Op::Exec("intrinsics::i32::checked_mul".parse().unwrap())) - } - Overflow::Overflowing => self.emit(Op::Exec( - "intrinsics::i32::overflowing_mul".parse().unwrap(), - )), - } - } - - /// Pops a u32 value off the stack, `a`, and performs `a * `. - /// - /// See the [Overflow] type for how overflow semantics can change the operation. - /// - /// Multiplying by zero is transformed into a sequence which drops the input value - /// and pushes a constant zero on the stack. - /// - /// Multiplying by one is a no-op. - #[inline] - pub fn mul_imm_u32(&mut self, imm: u32, overflow: Overflow) { - match imm { - 0 => { - self.emit_all(&[Op::Drop, Op::PushU32(0)]); - } - 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), - }); - } - } - } - - /// Pops a i32 value off the stack, `a`, and performs `a * `. - /// - /// See the [Overflow] type for how overflow semantics can change the operation. - /// - /// Multiplying by zero is transformed into a sequence which drops the input value - /// and pushes a constant zero on the stack. - /// - /// Multiplying by one is a no-op. - #[inline] - pub fn mul_imm_i32(&mut self, imm: i32, overflow: Overflow) { - match imm { - 0 => { - self.emit_all(&[Op::Drop, Op::PushU32(0)]); - } - 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()), - ]), - }, - } - } - - /// 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]); - } - - /// 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())); - } - - /// Pops a u32 value off the stack, `a`, and performs `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) { - assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit_all(&[Op::U32DivImm(imm), Op::U32Assert]); - } - - /// Pops a i32 value off the stack, `a`, and performs `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) { - 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()), - ]); - } - - /// 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); - } - - /// 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) { - assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit(Op::U32DivImm(imm)); - } - - /// 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]); - } - - /// Pops a u32 value off the stack, `a`, and performs `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) { - assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit_all(&[Op::U32ModImm(imm), Op::U32Assert]); - } - - /// 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); - } - - /// 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) { - assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit(Op::U32ModImm(imm)); - } - - /// 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]); - } - - /// 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) { - assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit_all(&[Op::U32DivModImm(imm), Op::U32Assert]); - } - - /// 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); - } - - /// 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) { - assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit(Op::U32DivModImm(imm)); - } - - /// 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); - } - - /// 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]); - } - - /// 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); - } - - /// 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]); - } - - /// 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); - } - - /// 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]); - } - - /// 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)); - } - - /// Pops two u32 values off the stack, `b` and `a`, and performs `a << b` - /// - /// 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); - } - - /// 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) { - assert!(imm < 32, "invalid shift value: must be < 32, got {imm}"); - self.emit(Op::U32ShlImm(imm)); - } - - /// Pops two u32 values off the stack, `b` and `a`, and performs `a >> b` - /// - /// 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); - } - - /// Pops two i32 values off the stack, `b` and `a`, and performs `a >> b` - /// - /// 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())); - } - - /// 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) { - assert!(imm < 32, "invalid shift value: must be < 32, got {imm}"); - self.emit(Op::U32ShrImm(imm)); - } - - /// 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: i32) { - assert!(imm < 32, "invalid shift value: must be < 32, got {imm}"); - self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::checked_shr".parse().unwrap()), - ]); - } - - /// Pops two u32 values off the stack, `b` and `a`, and rotates the bits of `a` left by `b` bits - /// - /// 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); - } - - /// 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) { - assert!(imm < 32, "invalid rotation value: must be < 32, got {imm}"); - self.emit(Op::U32RotlImm(imm)); - } - - /// Pops two u32 values off the stack, `b` and `a`, and rotates the bits of `a` right by `b` bits - /// - /// 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); - } - - /// 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) { - assert!(imm < 32, "invalid rotation value: must be < 32, got {imm}"); - self.emit(Op::U32RotrImm(imm)); - } - - /// 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); - } - - /// 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())); - } - - /// 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]); - } - - /// 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()), - ]); - } - - /// 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); - } - - /// 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())); - } - - /// 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]); - } - - /// 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()), - ]); - } -} diff --git a/codegen/masm/src/codegen/emit/int64.rs b/codegen/masm/src/codegen/emit/int64.rs deleted file mode 100644 index 65cf75eb6..000000000 --- a/codegen/masm/src/codegen/emit/int64.rs +++ /dev/null @@ -1,533 +0,0 @@ -use miden_hir::{Felt, FieldElement, Overflow}; - -use crate::masm::{self as masm, Op}; - -use super::{felt, OpEmitter, P}; - -#[allow(unused)] -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) { - // Copy the input operand for the check - self.copy_int64(); - // Assert that value is <= P - self.push_u64(P); - self.lte_u64(); - // Assert the value is in range, then multiply the 32-bit limbs to convert to felt - self.emit_all(&[Op::Assert, Op::Mul]); - } - - /// Convert a i64 value to felt. - /// - /// This operation will assert at runtime if the value is negative, or larger than the felt field. - pub fn i64_to_felt(&mut self) { - self.assert_unsigned_int64(); - self.u64_to_felt(); - } - - /// 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::PushU32(0), - Op::AssertEq, - // Check that the remaining bits fit in range - Op::Dup(0), - Op::Push(Felt::new(2u64.pow(n) - 1)), - Op::U32Lte, - ]); - } - - /// 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), - ]); - // 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), - ]); - // Select mask for remaining sign bits - // - // The mask should cover the u64 bits which must be set to 1 if - // the value is in range for the N-bit integer type. If the value - // is unsigned, the mask should be zero, so that comparing the - // mask for equality succeeds in that case - // - // The value bits are all of the non-sign bits, so for an N-bit - // 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, - ]); - } - - /// Truncate a i64/u64 value to a felt value - /// - /// This consumes the input value, and leaves a felt value on the stack. - /// - /// Truncation of field elements is not well-defined, as they have no specified - /// binary representation. However, the u64 representation we use consists of two - /// 32-bit limbs, and by multiplying the most significant limb by 2^32, and adding - /// in the least significant limb, modulo `P` at each step, we obtain an equivalent - /// felt to that we'd get from a typical bitwise truncation. - /// - /// Despite felt not having an exact bitwise representation (its range cannot be - /// represented precisely using a power of two), truncating a u64 to felt, and felt to - /// u32, is the same as truncating from u64 to u32. - /// - /// 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. - pub fn trunc_int64_to_felt(&mut self) { - self.emit_all(&[ - // Multiply the high bits by 2^32 to get the correct magnitude felt - // - // This multiplication is wrapped using `P` implicitly - Op::MulImm(felt::U32_FIELD_MODULUS), - // Add the two limbs together to obtain the felt representation of the u64 - // - // This addition is wrapped using `P` implicitly - Op::Add, - ]); - } - - /// Truncate this 64-bit value to N bits, where N is <= 32 - /// - /// This consumes the input value, and leaves an N-bit value on the stack. - /// - /// 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) { - assert_valid_integer_size!(n, 1, 32); - self.emit(Op::Drop); - match n { - 32 => (), - n => self.trunc_int32(n), - } - } - - /// Sign-extend a 64-bit value to an signed N-bit integer, where N >= 128 - pub fn sext_int64(&mut self, n: u32) { - assert_valid_integer_size!(n, 128, 256); - self.is_signed_int64(); - // Select the extension bits - self.select_int32(u32::MAX, 0); - // Pad out the missing bits - // - // Deduct 32 bits to account for the difference between u32 and u64 - self.pad_int32(n - 32); - } - - /// Zero-extend a 64-bit value to N-bits, where N >= 64 - pub fn zext_int64(&mut self, n: u32) { - 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); - } - - /// Assert that there is a valid 64-bit integer value on the operand stack - pub fn assert_int64(&mut self) { - self.emit(Op::U32Assert2); - } - - /// 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() - } - - /// Assert that the 64-bit value on the stack does not have its sign bit set. - pub fn assert_unsigned_int64(&mut self) { - // Assert that the sign bit is unset - self.assert_unsigned_int32() - } - - /// Assert that the 64-bit value on the stack is a valid i64 value - pub fn assert_i64(&mut self) { - // Copy the value on top of the stack - self.copy_int64(); - // 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); - } - - /// Duplicate the i64/u64 value on top of the stack - #[inline(always)] - pub fn copy_int64(&mut self) { - self.copy_int64_from(0) - } - - /// Duplicate a i64/u64 value to the top of the stack - /// - /// 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) { - assert_valid_stack_index!(n + 1); - // copy limbs such that the order is preserved - self.emit_n(2, Op::Dup(n + 1)); - } - - /// Move a 64-bit value to the top of the stack, i.e. `movup(N)` for 64-bit values - /// - /// The value `n` must be a valid stack index, and may not reference the last stack slot, - /// or this function will panic. - /// - /// A value of `0` has no effect. - #[inline] - pub fn move_int64_up(&mut self, n: u8) { - 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)); - } - 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), - ]); - } - } - } - - /// 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); - } - - /// Pushes a literal u64 value on the operand stack - #[inline] - pub fn push_u64(&mut self, value: u64) { - let (hi, lo) = to_raw_parts(value); - from_raw_parts(lo, hi, self.current_block()); - } - - /// 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::checked_lt".parse().unwrap())); - } - - /// 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::checked_lte".parse().unwrap())); - } - - /// 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::checked_gt".parse().unwrap())); - } - - /// 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::checked_gte".parse().unwrap())); - } - - /// 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::checked_eq".parse().unwrap())); - } - - /// 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::checked_eqz".parse().unwrap())); - } - - /// 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::checked_min".parse().unwrap())); - } - - /// 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::checked_max".parse().unwrap())); - } - - /// 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::checked_neq".parse().unwrap())); - } - - /// Pops two u64 values off the stack, `b` and `a`, and performs `a + b`. - /// - /// The semantics of this operation depend on the `overflow` setting: - /// - /// * There is no unchecked variant for u64, so wrapping is used instead - /// * When checked, both the operands and the result are validated to ensure - /// they are valid u64 values. - /// * Overflowing and wrapping variants follow the usual semantics, with the - /// exception that neither type validates the operands, it is assumed that - /// the caller has already done this. - /// - /// 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) { - match overflow { - Overflow::Checked => { - self.emit(Op::Exec("std::math::u64::checked_add".parse().unwrap())); - } - Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("std::math::u64::wrapping_add".parse().unwrap())); - } - Overflow::Overflowing => { - self.emit(Op::Exec("std::math::u64::overflowing_add".parse().unwrap())); - } - } - } - - /// Pops two u64 values off the stack, `b` and `a`, and performs `a - b`. - /// - /// The semantics of this operation depend on the `overflow` setting: - /// - /// * There is no unchecked variant for u64, so wrapping is used instead - /// * When checked, both the operands and the result are validated to ensure - /// they are valid u64 values. - /// * Overflowing and wrapping variants follow the usual semantics, with the - /// exception that neither type validates the operands, it is assumed that - /// the caller has already done this. - /// - /// 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) { - match overflow { - Overflow::Checked => { - self.emit(Op::Exec("std::math::u64::checked_sub".parse().unwrap())); - } - Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("std::math::u64::wrapping_sub".parse().unwrap())); - } - Overflow::Overflowing => { - self.emit(Op::Exec("std::math::u64::overflowing_sub".parse().unwrap())); - } - } - } - - /// Pops two u64 values off the stack, `b` and `a`, and performs `a * b`. - /// - /// The semantics of this operation depend on the `overflow` setting: - /// - /// * There is no unchecked variant for u64, so wrapping is used instead - /// * When checked, both the operands and the result are validated to ensure - /// they are valid u64 values. - /// * Overflowing and wrapping variants follow the usual semantics, with the - /// exception that neither type validates the operands, it is assumed that - /// the caller has already done this. - /// - /// 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) { - match overflow { - Overflow::Checked => { - self.emit(Op::Exec("std::math::u64::checked_mul".parse().unwrap())); - } - Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("std::math::u64::wrapping_mul".parse().unwrap())); - } - Overflow::Overflowing => { - self.emit(Op::Exec("std::math::u64::overflowing_mul".parse().unwrap())); - } - } - } - - /// Pops two u64 values off the stack, `b` and `a`, and pushes the result of `a / b` on the stack. - /// - /// Both the operands and result are validated to ensure they are valid u64 values. - #[inline] - pub fn checked_div_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::checked_div".parse().unwrap())); - } - - /// Pops two u64 values off the stack, `b` and `a`, and pushes the result of `a / b` on the stack. - /// - /// 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::unchecked_div".parse().unwrap())); - } - - /// Pops two u64 values off the stack, `b` and `a`, and pushes the result of `a % b` on the stack. - /// - /// Both the operands and result are validated to ensure they are valid u64 values. - #[inline] - pub fn checked_mod_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::checked_mod".parse().unwrap())); - } - - /// Pops two u64 values off the stack, `b` and `a`, and pushes the result of `a % b` on the stack. - /// - /// 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::unchecked_mod".parse().unwrap())); - } - - /// Pops two u64 values off the stack, `b` and `a`, and pushes `a / b`, then `a % b` on the stack. - /// - /// Both the operands and result are validated to ensure they are valid u64 values. - #[inline] - pub fn checked_divmod_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::checked_divmod".parse().unwrap())); - } - - /// Pops two u64 values off the stack, `b` and `a`, and pushes `a / b`, then `a % b` on the stack. - /// - /// 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::unchecked_divmod".parse().unwrap(), - )); - } - - /// 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::checked_and".parse().unwrap())); - } - - /// 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::checked_or".parse().unwrap())); - } - - /// 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::checked_xor".parse().unwrap())); - } - - /// Pops a u32 value, `b`, and a u64 value, `a`, off the stack and pushes `a << b` on the stack. - /// - /// Overflow bits are truncated. - /// - /// The operation will trap if the shift value is > 63. - #[inline] - pub fn shl_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::unchecked_shl".parse().unwrap())); - } - - /// Pops a u32 value, `b`, and a u64 value, `a`, off the stack and pushes `a >> b` on the stack. - /// - /// Overflow bits are truncated. - /// - /// The operation will trap if the shift value is > 63. - #[inline] - pub fn shr_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::unchecked_shr".parse().unwrap())); - } - - /// Pops a u32 value, `b`, and a u64 value, `a`, off the stack and rotates the bitwise - /// representation of `a` left `b` bits. Any values that are rotated past the most significant - /// bit, wrap around to the least significant bit. - /// - /// The operation will trap if the rotation value is > 63. - #[inline] - pub fn rotl_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::unchecked_rotl".parse().unwrap())); - } - - /// Pops a u32 value, `b`, and a u64 value, `a`, off the stack and rotates the bitwise - /// representation of `a` right `b` bits. Any values that are rotated past the least significant - /// bit, wrap around to the most significant bit. - /// - /// The operation will trap if the rotation value is > 63. - #[inline] - pub fn rotr_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::unchecked_rotr".parse().unwrap())); - } -} - -/// Decompose a u64 value into it's raw 32-bit limb components -/// -/// Returns `(hi, lo)`, where `hi` is the most significant limb, -/// and `lo` is the least significant limb. -#[inline(always)] -pub fn to_raw_parts(value: u64) -> (u32, u32) { - let bytes = value.to_le_bytes(); - let hi = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); - let lo = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]); - (hi, lo) -} - -/// 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)])); -} diff --git a/codegen/masm/src/codegen/emit/mem.rs b/codegen/masm/src/codegen/emit/mem.rs deleted file mode 100644 index d42729772..000000000 --- a/codegen/masm/src/codegen/emit/mem.rs +++ /dev/null @@ -1,949 +0,0 @@ -use miden_hir::{StructType, Type}; - -use crate::masm::{NativePtr, Op}; - -use super::OpEmitter; - -/// Allocation -impl<'a> OpEmitter<'a> { - /// Allocate a procedure-local memory slot of sufficient size to store a value - /// indicated by the given pointer type, i.e the pointee type dictates the - /// amount of memory allocated. - /// - /// The address of that slot is placed on the operand stack. - pub fn alloca(&mut self, ptr: &Type) { - match ptr { - Type::Ptr(pointee) => { - let local = self.function.alloc_local(pointee.as_ref().clone()); - self.emit(Op::LocAddr(local)); - self.stack.push(ptr.clone()); - } - ty => panic!("expected a pointer type, got {ty}"), - } - } -} - -/// Loads -impl<'a> OpEmitter<'a> { - /// Load a value of corresponding to the pointee type of a pointer operand on the stack. - /// - /// 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) { - let ptr = self.stack.pop().expect("operand stack is empty"); - match ptr.ty() { - Type::Ptr(_) => { - // Converet the pointer to a native pointer representation - self.emit_native_ptr(); - 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), - ty @ (Type::I16 | Type::U16 | Type::U8 | Type::I8 | Type::I1) => { - self.load_word(None); - self.trunc_int32(ty.size_in_bits() as u32); - } - ty => todo!("support for loading {ty} is not yet implemented"), - } - self.stack.push(ty); - } - ty if !ty.is_pointer() => { - panic!("invalid operand to load: expected pointer, got {ty}") - } - ty => unimplemented!("load support for pointers of type {ty} is not implemented"), - } - } - - /// 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) { - 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::I16 | Type::U16 | Type::U8 | Type::I8 | Type::I1 => { - self.load_word(Some(ptr)); - self.trunc_int32(ty.size_in_bits() as u32); - } - ty => todo!("support for loading {ty} is not yet implemented"), - } - self.stack.push(ty); - } - - /// Emit a sequence of instructions to translate a raw pointer value to - /// a native pointer value, as a triple of `(waddr, index, offset)`, in - /// that order on the stack. - /// - /// 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), - ]); - } - - /// 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) { - if let Some(imm) = ptr { - return self.load_felt_imm(imm); - } - - self.emit(Op::Exec("intrinsics::mem::load_felt".parse().unwrap())); - } - - fn load_felt_imm(&mut self, ptr: NativePtr) { - assert!( - ptr.is_element_aligned(), - "felt values must be naturally aligned" - ); - match ptr.index { - 0 => self.emit(Op::MemLoadImm(ptr.waddr)), - 1 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Movup(4), - Op::Movup(4), - Op::Drop, - Op::Drop, - Op::Drop, - ]); - } - 2 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Drop, - Op::Drop, - Op::Swap(1), - Op::Drop, - ]); - } - 3 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Drop, - Op::Drop, - Op::Drop, - ]); - } - _ => unreachable!(), - } - } - - /// Loads a single 32-bit machine word, i.e. a single field element, not the Miden notion of a word - /// - /// Expects a native pointer triplet on the stack if an immediate address is not given. - fn load_word(&mut self, ptr: Option) { - if let Some(imm) = ptr { - return self.load_word_imm(imm); - } - - self.emit(Op::Exec("intrinsics::mem::load_sw".parse().unwrap())); - } - - /// Loads a single 32-bit machine word from the given immediate address. - fn load_word_imm(&mut self, ptr: NativePtr) { - 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 => { - 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, - ]); - } - 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(&[ - // 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, - ]); - } - 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(&[ - // 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, - ]); - } - 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 - 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, - ]); - } - _ => unreachable!(), - } - } - - /// Load a pair of machine words (32-bit elements) to the operand stack - fn load_double_word(&mut self, ptr: Option) { - if let Some(imm) = ptr { - return self.load_double_word_imm(imm); - } - - self.emit(Op::Exec("intrinsics::mem::load_dw".parse().unwrap())); - } - - fn load_double_word_imm(&mut self, ptr: NativePtr) { - 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, - ]); - } - 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); - } - 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, - ]); - } - 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); - } - 2 if aligned => { - self.emit_all(&[ - // Load quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop unused words - Op::Drop, - Op::Drop, - ]); - } - 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); - } - 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, - ]); - } - 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); - } - _ => 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) { - if let Some(imm) = ptr { - return self.load_quad_word_imm(imm); - } - self.emit(Op::Exec("intrinsics::mem::load_qw".parse().unwrap())); - } - - fn load_quad_word_imm(&mut self, ptr: NativePtr) { - // 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 => { - // 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); - } - 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, - ]); - } - 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); - } - 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, - ]); - } - 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); - } - 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, - ]); - } - 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); - } - _ => unimplemented!("unaligned loads are not yet implemented: {ptr:#?}"), - } - } - - /// This handles emitting code that handles aligning an unaligned double machine-word value - /// which is split across three machine words (field elements). - /// - /// To recap: - /// - /// * A machine word is a 32-bit chunk stored in a single field element - /// * A double word is a pair of 32-bit chunks - /// * A quad word is a quartet of 32-bit chunks (i.e. a Miden "word") - /// * An unaligned double-word requires three 32-bit chunks to represent, - /// since the first chunk does not contain a full 32-bits, so an extra is - /// needed to hold those bits. - /// - /// As an example, assume the pointer we are dereferencing is a u64 value, - /// which has 8-byte alignment, and the value is stored 40 bytes from the - /// nearest quad-word-aligned boundary. To load the value, we must fetch - /// the full quad-word from the aligned address, drop the first word, as - /// it is unused, and then recombine the 64 bits we need spread across - /// the remaining three words to obtain the double-word value we actually want. - /// - /// The data, on the stack, is shown below: - /// - /// ```text,ignore - /// # If we visualize which bytes are contained in each 32-bit chunk on the stack, we get: - /// [0..=4, 5..=8, 9..=12] - /// - /// # These byte indices are relative to the nearest word-aligned address, in the same order - /// # as they would occur in a byte-addressable address space. The significance of each byte - /// # depends on the value being dereferenced, but Miden is a little-endian machine, so typically - /// # the most significant bytes come first (i.e. also commonly referred to as "high" vs "low" bits). - /// # - /// # If we visualize the layout of the bits of our u64 value spread across the three chunks, we get: - /// [00000000111111111111111111111111, 111111111111111111111111111111, 11111111111111111111111100000000] - /// ``` - /// - /// As illustrated above, what should be a double-word value is occupying three words. To "realign" the - /// value, i.e. ensure that it is naturally aligned and fits in two words, we 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) { - // 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), - ]); - } - - /// This handles emitting code that handles aligning an unaligned quad machine-word value - /// which is split across five machine words (field elements). - /// - /// To recap: - /// - /// * A machine word is a 32-bit chunk stored in a single field element - /// * A double word is a pair of 32-bit chunks - /// * A quad word is a quartet of 32-bit chunks (i.e. a Miden "word") - /// * An unaligned quad-word requires five 32-bit chunks to represent, - /// since the first chunk does not contain a full 32-bits, so an extra is - /// needed to hold those bits. - /// - /// 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) { - // 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), - ]); - } -} - -/// Stores -impl<'a> OpEmitter<'a> { - /// Store a value of type `value` to the address in the Miden address space - /// which corresponds to a pointer in the IR's byte-addressable address space. - /// - /// The type of the pointer is given as `ptr`, and can be used for both validation and - /// determining alignment. - pub fn store(&mut self) { - 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(); - assert!( - ptr_ty.is_pointer(), - "expected load operand to be a pointer, got {ptr_ty}" - ); - let value_ty = value.ty(); - assert!( - !value_ty.is_zst(), - "cannot store a zero-sized type in memory" - ); - match ptr_ty { - Type::Ptr(_) => { - // Converet the pointer to a native pointer representation - self.emit_native_ptr(); - 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), - ty => unimplemented!( - "invalid store: support for storing {ty} has not been implemented" - ), - } - } - ty if !ty.is_pointer() => { - panic!("invalid operand to store: expected pointer, got {ty}") - } - ty => unimplemented!("store support for pointers of type {ty} is not implemented"), - } - } - - /// 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) { - 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)), - ty => { - unimplemented!("invalid store: support for storing {ty} has not been implemented") - } - } - } - - /// Copy `count * sizeof(*ty)` from a source address to a destination address. - /// - /// The order of operands on the stack is `src`, `dst`, then `count`. - /// - /// The addresses on the stack are interpreted based on the pointer type: native pointers are - /// in the Miden address space; non-native pointers are assumed to be in the IR's byte addressable - /// address space, and require translation. - /// - /// The semantics of this instruction are as follows: - /// - /// * The `` - pub fn memcpy(&mut self) { - 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"); - assert_eq!(count.ty(), Type::U32, "expected count operand to be a u32"); - let ty = src.ty(); - assert_eq!( - ty, - dst.ty(), - "expected src and dst operands to have the same type" - ); - match ty { - Type::Ptr(ref _pointee) => { - todo!() - } - ty if !ty.is_pointer() => { - panic!("invalid operand to memcpy: expected pointer, got {ty}") - } - ty => unimplemented!("memcpy support for pointers of type {ty} is not implemented"), - } - } - - fn store_quad_word(&mut self, _ptr: Option) { - todo!() - } - - fn store_double_word(&mut self, _ptr: Option) { - todo!() - } - - fn store_word(&mut self, _ptr: Option) { - todo!() - } - - fn store_felt(&mut self, _ptr: Option) { - todo!() - } - - fn store_small(&mut self, _ty: &Type, _ptr: Option) { - todo!() - } - - fn store_array(&mut self, _element_ty: &Type, _ptr: Option) { - todo!() - } - - fn store_struct(&mut self, _ty: &StructType, _ptr: Option) { - todo!() - } -} diff --git a/codegen/masm/src/codegen/emit/mod.rs b/codegen/masm/src/codegen/emit/mod.rs deleted file mode 100644 index 11f350930..000000000 --- a/codegen/masm/src/codegen/emit/mod.rs +++ /dev/null @@ -1,1880 +0,0 @@ -/// The field modulus for Miden's prime field -pub const P: u64 = (2u128.pow(64) - 2u128.pow(32) + 1) as u64; - -/// Assert that an argument specifying an integer size in bits follows the rules about what -/// integer sizes we support as a general rule. -macro_rules! assert_valid_integer_size { - ($n:ident) => { - assert!( - $n > 0, - "invalid integer size: size in bits must be non-zero" - ); - assert!( - $n.is_power_of_two(), - "invalid integer size: size in bits must be a power of two, got {}", - $n - ); - }; - - ($n:ident, $min:literal) => { - assert_valid_integer_size!($n); - assert!( - $n >= $min, - "invalid integer size: expected size in bits greater than or equal to {}, got {}", - $n, - $min - ); - }; - - ($n:ident, $min:ident) => { - assert_valid_integer_size!($n); - assert!( - $n >= $min, - "invalid integer size: expected size in bits greater than or equal to {}, got {}", - $n, - $min - ); - }; - - ($n:ident, $min:literal, $max:literal) => { - assert_valid_integer_size!($n, $min); - assert!( - $n <= $max, - "invalid integer size: expected size in bits less than or equal to {}, got {}", - $n, - $max - ); - }; - - ($n:ident, $min:ident, $max:literal) => { - assert_valid_integer_size!($n, $min); - assert!( - $n <= $max, - "invalid integer size: expected size in bits less than or equal to {}, got {}", - $n, - $max - ); - }; -} - -/// Assert that an argument specifying a zero-based stack index does not access out of bounds -macro_rules! assert_valid_stack_index { - ($idx:ident) => { - assert!( - $idx < 16, - "invalid stack index: only the first 16 elements on the stack are directly accessible, got {}", - $idx - ); - }; - - ($idx:expr) => { - assert!( - ($idx) < 16, - "invalid stack index: only the first 16 elements on the stack are directly accessible, got {}", - $idx - ); - } -} - -pub mod binary; -pub mod felt; -pub mod int128; -pub mod int32; -pub mod int64; -pub mod mem; -pub mod primop; -pub mod smallint; -pub mod unary; - -use core::ops::{Deref, DerefMut}; -use miden_hir::{self as hir, Immediate, Type}; - -use crate::masm::{self as masm, Op}; - -use super::{Operand, OperandStack}; - -/// This structure is used to emit the Miden Assembly ops corresponding to an IR instruction. -/// -/// When dropped, it ensures that the operand stack is updated to reflect the results of the -/// instruction it was created on behalf of. -pub struct InstOpEmitter<'a> { - dfg: &'a hir::DataFlowGraph, - inst: hir::Inst, - emitter: OpEmitter<'a>, -} -impl<'a> InstOpEmitter<'a> { - #[inline(always)] - pub fn new( - function: &'a mut masm::Function, - dfg: &'a hir::DataFlowGraph, - inst: hir::Inst, - block: masm::BlockId, - stack: &'a mut OperandStack, - ) -> Self { - Self { - dfg, - inst, - emitter: OpEmitter::new(function, block, stack), - } - } - - pub fn exec(&mut self, callee: hir::FunctionIdent) { - let import = self.dfg.get_import(&callee).unwrap(); - self.emitter.exec(import); - } - - pub fn syscall(&mut self, callee: hir::FunctionIdent) { - let import = self.dfg.get_import(&callee).unwrap(); - self.emitter.syscall(import); - } - - #[inline(always)] - pub fn value_type(&self, value: hir::Value) -> &Type { - self.dfg.value_type(value) - } - - #[inline(always)] - pub fn dfg<'c, 'b: 'c>(&'b self) -> &'c hir::DataFlowGraph { - self.dfg - } -} -impl<'a> Deref for InstOpEmitter<'a> { - type Target = OpEmitter<'a>; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.emitter - } -} -impl<'a> DerefMut for InstOpEmitter<'a> { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.emitter - } -} -impl<'a> Drop for InstOpEmitter<'a> { - fn drop(&mut self) { - let results = self.dfg.inst_results(self.inst); - for (i, result) in results.iter().copied().rev().enumerate() { - self.emitter.stack.rename(i, result); - } - } -} - -/// This structure is used to emit Miden Assembly ops into a given function and block. -/// -/// The [OpEmitter] carries limited context of its own, and expects to receive arguments -/// to it's various builder functions to provide necessary context for specific constructs. -pub struct OpEmitter<'a> { - stack: &'a mut OperandStack, - function: &'a mut masm::Function, - current_block: masm::BlockId, -} -impl<'a> OpEmitter<'a> { - #[inline(always)] - pub fn new( - function: &'a mut masm::Function, - block: masm::BlockId, - stack: &'a mut OperandStack, - ) -> Self { - Self { - stack, - function, - current_block: block, - } - } - - #[cfg(test)] - #[inline(always)] - pub fn stack_len(&self) -> usize { - self.stack.len() - } - - #[inline(always)] - pub fn stack<'c, 'b: 'c>(&'b self) -> &'c OperandStack { - self.stack - } - - #[inline(always)] - pub fn stack_mut<'c, 'b: 'c>(&'b mut self) -> &'c mut OperandStack { - self.stack - } - /// Emit `op` to the current block - #[inline(always)] - pub fn emit(&mut self, op: masm::Op) { - self.current_block().push(op) - } - - /// Emit `n` copies of `op` to the current block - #[inline(always)] - pub fn emit_n(&mut self, count: usize, op: masm::Op) { - self.current_block().push_n(count, op); - } - - /// Emit `ops` to the current block - #[inline(always)] - pub fn emit_all(&mut self, ops: &[masm::Op]) { - self.current_block().extend_from_slice(ops); - } - - /// Emit `n` copies of the sequence `ops` to the current block - #[inline(always)] - pub fn emit_repeat(&mut self, count: usize, ops: &[masm::Op]) { - self.current_block().push_repeat(ops, count); - } - - /// Emit `n` copies of the sequence `ops` to the current block - #[inline] - pub fn emit_template(&mut self, count: usize, template: F) - where - F: Fn(usize) -> [Op; N], - { - let block = self.current_block(); - for n in 0..count { - block.extend_from_slice(&template(n)); - } - } - - /// Push an immediate value on the operand stack - /// - /// This has no effect on the state of the emulated operand stack - #[inline] - pub fn push_immediate(&mut self, imm: Immediate) { - 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::I128(i) => self.push_i128(i), - Immediate::Felt(i) => self.emit(Op::Push(i)), - 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) { - let imm = imm.into(); - self.push_immediate(imm); - self.stack.push(imm); - } - - #[inline(always)] - pub fn pop(&mut self) -> Option { - self.stack.pop() - } - - /// Push an operand on the stack - #[inline(always)] - pub fn push>(&mut self, operand: O) { - self.stack.push(operand) - } - - /// Duplicate an item on the stack to the top - #[inline] - #[track_caller] - pub fn dup(&mut self, i: u8) { - assert_valid_stack_index!(i); - let index = i as usize; - let i = self.stack.effective_index(index) as u8; - self.stack.dup(index); - // Emit low-level instructions corresponding to the operand we duplicated - let last = self.stack.peek().expect("operand stack is empty"); - let n = last.size(); - let offset = (n - 1) as u8; - for _ in 0..n { - self.emit(Op::Dup(i + offset)); - } - } - - /// Move an item on the stack to the top - #[inline] - #[track_caller] - pub fn movup(&mut self, i: u8) { - assert_valid_stack_index!(i); - let index = i as usize; - let i = self.stack.effective_index(index) as u8; - self.stack.movup(index); - // Emit low-level instructions corresponding to the operand we moved - let moved = self.stack.peek().expect("operand stack is empty"); - let n = moved.size(); - let offset = (n - 1) as u8; - for _ in 0..n { - self.emit(Op::Movup(i + offset)); - } - } - - /// Move an item from the top of the stack to the `n`th position - #[inline] - #[track_caller] - pub fn movdn(&mut self, i: u8) { - assert_valid_stack_index!(i); - let index = i as usize; - let i = self.stack.effective_index_inclusive(index) as u8; - let top = self.stack.peek().expect("operand stack is empty"); - let top_size = top.size(); - self.stack.movdn(index); - // Emit low-level instructions corresponding to the operand we moved - for _ in 0..top_size { - self.emit(Op::Movdn(i)); - } - } - - /// Swap an item with the top of the stack - #[inline] - #[track_caller] - pub fn swap(&mut self, i: u8) { - assert!(i > 0, "swap requires a non-zero index"); - assert_valid_stack_index!(i); - let index = i as usize; - let src = self.stack[0].size() as u8; - let dst = self.stack[index].size() as u8; - let i = self.stack.effective_index(index) as u8; - self.stack.swap(index); - match (src, dst) { - (1, 1) => { - self.emit(Op::Swap(i)); - } - (1, n) if i == 1 => { - // We can simply move the top element below the `dst` operand - self.emit(Op::Movdn(i + (n - 1))); - } - (n, 1) if i == n => { - // We can simply move the `dst` element to the top - self.emit(Op::Movup(i)); - } - (n, m) if i == n => { - // We can simply move `dst` down - for _ in 0..n { - self.emit(Op::Movdn(i + (m - 1))); - } - } - (n, m) => { - assert!(i >= n); - let offset = m - 1; - for _ in 0..n { - self.emit(Op::Movdn(i + offset)); - } - 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)); - } - i => { - for _ in 0..m { - self.emit(Op::Movup(i)); - } - } - } - } - } - } - - /// Drop the top operand on the stack - #[inline] - #[track_caller] - pub fn drop(&mut self) { - let elem = self.stack.pop().expect("operand stack is empty"); - match elem.size() { - 1 => { - self.emit(Op::Drop); - } - 4 => { - self.emit(Op::Dropw); - } - n => { - for _ in 0..n { - self.emit(Op::Drop); - } - } - } - } - - /// Drop the top `n` operands on the stack - #[inline] - #[track_caller] - pub fn dropn(&mut self, n: usize) { - 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); - } - 4 => { - self.emit(Op::Dropw); - } - n => { - self.emit_n(n / 4, Op::Dropw); - self.emit_n(n % 4, Op::Drop); - } - } - } - - /// Remove all but the top `n` values on the operand stack - pub fn truncate_stack(&mut self, n: usize) { - let stack_size = self.stack.len(); - let num_to_drop = stack_size - n; - - if num_to_drop == 0 { - return; - } - - 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); - return; - } - - // This is the common case, and can be handled simply - // by moving the value to the bottom of the stack and - // dropping everything in-between - if n == 1 { - match stack_size { - 2 => { - self.swap(1); - self.drop(); - } - n => { - self.movdn(n as u8 - 1); - self.dropn(n - 1); - } - } - return; - } - - // TODO: This is a very neive algorithm for clearing - // the stack of all but the top `n` values, we should - // 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); - } - } - - /// Remove the `n`th value from the top of the operand stack - pub fn drop_operand_at_position(&mut self, n: usize) { - match n { - 0 => { - self.drop(); - } - 1 => { - self.swap(1); - self.drop(); - } - n => { - self.movup(n as u8); - self.drop(); - } - } - } - - /// Copy the `n`th operand on the stack, and make it the `m`th operand on the stack. - /// - /// If the operand is for a commutative, binary operator, indicated by `is_commutative_binary_operand`, - /// and the desired position is just below the top of stack, this function may leave it on top of the - /// stack instead, since the order of the operands is not strict. This can result in fewer stack - /// manipulation instructions in some scenarios. - pub fn copy_operand_to_position( - &mut self, - n: usize, - m: usize, - is_commutative_binary_operand: bool, - ) { - match (n, m) { - (0, 0) => { - self.dup(0); - } - (actual, 0) => { - self.dup(actual as u8); - } - (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); - } else { - self.dup(actual as u8); - } - } - (actual, expected) => { - self.dup(actual as u8); - self.movdn(expected as u8); - } - } - } - - /// Make the `n`th operand on the stack, the `m`th operand on the stack. - /// - /// If the operand is for a commutative, binary operator, indicated by `is_commutative_binary_operand`, - /// and the desired position is one of the first two items on the stack, this function may leave the - /// operand in it's current position if it is already one of the first two items on the stack, - /// since the order of the operands is not strict. This can result in fewer stack manipulation - /// instructions in some scenarios. - pub fn move_operand_to_position( - &mut self, - n: usize, - m: usize, - is_commutative_binary_operand: bool, - ) { - match (n, m) { - (n, m) if n == m => (), - (1, 0) | (0, 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.swap(1); - } - } - (actual, 0) => { - self.movup(actual as u8); - } - (actual, 1) => { - self.movup(actual as u8); - self.swap(1); - } - (actual, expected) => { - self.movup(actual as u8); - self.movdn(expected as u8); - } - } - } - - /// Get mutable access to the current block we're emitting to - #[inline(always)] - pub fn current_block<'c, 'b: 'c>(&'b mut self) -> &'c mut masm::Block { - self.function.body.block_mut(self.current_block) - } - - #[allow(unused)] - #[inline] - pub fn switch_to_block(&mut self, block: masm::BlockId) -> masm::BlockId { - let prev = self.current_block; - self.current_block = block; - prev - } - - #[allow(unused)] - #[inline(always)] - pub fn position(&self) -> masm::BlockId { - self.current_block - } -} - -#[cfg(test)] -mod tests { - use miden_hir::{AbiParam, Felt, FieldElement, Overflow, Signature, Type}; - - use super::*; - use crate::{ - codegen::{OperandStack, TypedValue}, - masm::Function, - }; - - #[test] - fn op_emitter_stack_manipulation_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - let three = Immediate::U8(3); - 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); - - { - 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!(emitter.stack()[0], five); - assert_eq!(emitter.stack()[1], four); - assert_eq!(emitter.stack()[2], three); - assert_eq!(emitter.stack()[3], two); - assert_eq!(emitter.stack()[4], one); - - emitter.dup(0); - assert_eq!(emitter.stack()[0], five); - assert_eq!(emitter.stack()[1], five); - assert_eq!(emitter.stack()[2], four); - assert_eq!(emitter.stack()[3], three); - assert_eq!(emitter.stack()[4], two); - - { - 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!(emitter.stack().effective_index(3), 6); - emitter.dup(3); - assert_eq!(emitter.stack()[0], three); - assert_eq!(emitter.stack()[1], five); - assert_eq!(emitter.stack()[2], five); - assert_eq!(emitter.stack()[3], four); - assert_eq!(emitter.stack()[4], three); - assert_eq!(emitter.stack()[5], two); - - { - 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!(emitter.stack().effective_index(1), 1); - emitter.swap(1); - assert_eq!(emitter.stack().effective_index(1), 2); - assert_eq!(emitter.stack()[0], five); - assert_eq!(emitter.stack()[1], three); - assert_eq!(emitter.stack()[2], five); - assert_eq!(emitter.stack()[3], four); - assert_eq!(emitter.stack()[4], three); - assert_eq!(emitter.stack()[5], two); - - { - 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!(emitter.stack().effective_index(3), 5); - emitter.swap(3); - assert_eq!(emitter.stack()[0], four); - assert_eq!(emitter.stack()[1], three); - assert_eq!(emitter.stack()[2], five); - assert_eq!(emitter.stack()[3], five); - assert_eq!(emitter.stack()[4], three); - assert_eq!(emitter.stack()[5], two); - - { - 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] - } - - emitter.movdn(2); - assert_eq!(emitter.stack()[0], three); - assert_eq!(emitter.stack()[1], five); - assert_eq!(emitter.stack()[2], four); - assert_eq!(emitter.stack()[3], five); - assert_eq!(emitter.stack()[4], three); - assert_eq!(emitter.stack()[5], two); - - { - 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] - } - - emitter.movup(2); - assert_eq!(emitter.stack()[0], four); - assert_eq!(emitter.stack()[1], three); - assert_eq!(emitter.stack()[2], five); - assert_eq!(emitter.stack()[3], five); - assert_eq!(emitter.stack()[4], three); - assert_eq!(emitter.stack()[5], two); - - { - 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] - } - - emitter.drop(); - assert_eq!(emitter.stack()[0], three); - assert_eq!(emitter.stack()[1], five); - assert_eq!(emitter.stack()[2], five); - assert_eq!(emitter.stack()[3], three); - assert_eq!(emitter.stack()[4], two); - assert_eq!(emitter.stack()[5], one); - - { - 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] - } - - emitter.copy_operand_to_position(5, 3, false); - 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], three); - assert_eq!(emitter.stack()[5], two); - - emitter.drop_operand_at_position(4); - 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); - assert_eq!(emitter.stack()[0], three); - assert_eq!(emitter.stack()[1], five); - assert_eq!(emitter.stack()[2], two); - assert_eq!(emitter.stack()[3], five); - assert_eq!(emitter.stack()[4], one); - } - - #[test] - fn op_emitter_copy_operand_to_position_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let v14 = hir::Value::from_u32(14); - let v13 = hir::Value::from_u32(13); - let v15 = hir::Value::from_u32(15); - let v11 = hir::Value::from_u32(11); - let v10 = hir::Value::from_u32(10); - let v1 = hir::Value::from_u32(1); - let v2 = hir::Value::from_u32(2); - - emitter.push(TypedValue { - value: v2, - ty: Type::U32, - }); - emitter.push(TypedValue { - value: v1, - ty: Type::U32, - }); - emitter.push(TypedValue { - value: v10, - ty: Type::U32, - }); - emitter.push(TypedValue { - value: v11, - ty: Type::U32, - }); - emitter.push(TypedValue { - value: v15, - ty: Type::U32, - }); - emitter.push(TypedValue { - value: v13, - ty: Type::U32, - }); - emitter.push(TypedValue { - value: v14, - ty: Type::U32, - }); - - assert_eq!(emitter.stack().find(&v10), Some(4)); - - assert_eq!(emitter.stack()[4], v10); - assert_eq!(emitter.stack()[2], v15); - emitter.copy_operand_to_position(4, 2, false); - assert_eq!(emitter.stack()[5], v10); - assert_eq!(emitter.stack()[2], v10); - - { - 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)); - } - } - - #[test] - fn op_emitter_u32_add_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.add_imm(one, Overflow::Checked); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.add(Overflow::Checked); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - - emitter.add_imm(one, Overflow::Overflowing); - 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); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I1); - assert_eq!(emitter.stack()[1], Type::U32); - } - - #[test] - fn op_emitter_u32_sub_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.sub_imm(one, Overflow::Checked); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.sub(Overflow::Checked); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - - emitter.sub_imm(one, Overflow::Overflowing); - 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); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I1); - assert_eq!(emitter.stack()[1], Type::U32); - } - - #[test] - fn op_emitter_u32_mul_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.mul_imm(one, Overflow::Checked); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.mul(Overflow::Checked); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - - emitter.mul_imm(one, Overflow::Overflowing); - 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); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I1); - assert_eq!(emitter.stack()[1], Type::U32); - } - - #[test] - fn op_emitter_u32_eq_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.eq_imm(two); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I1); - assert_eq!(emitter.stack()[1], one); - - emitter.assert(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], one); - - emitter.dup(0); - emitter.eq(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_u32_neq_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.neq_imm(two); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I1); - assert_eq!(emitter.stack()[1], one); - - emitter.assertz(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], one); - - emitter.dup(0); - emitter.neq(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_i1_and_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let t = Immediate::I1(true); - let f = Immediate::I1(false); - - emitter.literal(t); - emitter.literal(f); - - emitter.and_imm(t); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I1); - assert_eq!(emitter.stack()[1], t); - - emitter.and(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_i1_or_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let t = Immediate::I1(true); - let f = Immediate::I1(false); - - emitter.literal(t); - emitter.literal(f); - - emitter.or_imm(t); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I1); - assert_eq!(emitter.stack()[1], t); - - emitter.or(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_i1_xor_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let t = Immediate::I1(true); - let f = Immediate::I1(false); - - emitter.literal(t); - emitter.literal(f); - - emitter.xor_imm(t); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I1); - assert_eq!(emitter.stack()[1], t); - - emitter.xor(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_i1_not_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let t = Immediate::I1(true); - - emitter.literal(t); - - emitter.not(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_u32_gt_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.gt_imm(two); - 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(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_u32_gte_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.gte_imm(two); - 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(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_u32_lt_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.lt_imm(two); - 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(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_u32_lte_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.lte_imm(two); - 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(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_u32_checked_div_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.checked_div_imm(two); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.checked_div(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_unchecked_div_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.unchecked_div_imm(two); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.unchecked_div(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_checked_mod_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.checked_mod_imm(two); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.checked_mod(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_unchecked_mod_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.unchecked_mod_imm(two); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.unchecked_mod(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_checked_divmod_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.checked_divmod_imm(two); - 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(); - 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); - } - - #[test] - fn op_emitter_u32_unchecked_divmod_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.unchecked_divmod_imm(two); - 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(); - 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); - } - - #[test] - fn op_emitter_u32_exp_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.exp_imm(two); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.exp(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_band_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.band_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.band(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_bor_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.bor_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.bor(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_bxor_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.bxor_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.bxor(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_shl_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.shl_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.shl(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_shr_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.shr_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.shr(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_rotl_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.rotl_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.rotl(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_rotr_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.rotr_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.rotr(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_min_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.min_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.min(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_max_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - - emitter.max_imm(one); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::U32); - assert_eq!(emitter.stack()[1], one); - - emitter.max(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_trunc_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let max = Immediate::U32(u32::MAX); - - emitter.literal(max); - - emitter.trunc(&Type::U16); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U16); - } - - #[test] - fn op_emitter_u32_zext_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let one = Immediate::U16(1); - - emitter.literal(one); - - emitter.zext(&Type::U32); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_sext_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let num = Immediate::I16(-128); - - emitter.literal(num); - - emitter.sext(&Type::I32); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I32); - } - - #[test] - fn op_emitter_u32_cast_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let num = Immediate::U32(128); - - emitter.literal(num); - - emitter.cast(&Type::I32); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I32); - } - - #[test] - fn op_emitter_u32_inttoptr_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let addr = Immediate::U32(128); - let ptr = Type::Ptr(Box::new(Type::Array(Box::new(Type::U64), 8))); - - emitter.literal(addr); - - emitter.inttoptr(&ptr); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], ptr); - } - - #[test] - fn op_emitter_u32_is_odd_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let num = Immediate::U32(128); - - emitter.literal(num); - - emitter.is_odd(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::I1); - } - - #[test] - fn op_emitter_u32_popcnt_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let num = Immediate::U32(128); - - emitter.literal(num); - - emitter.popcnt(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_bnot_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let num = Immediate::U32(128); - - emitter.literal(num); - - emitter.bnot(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_pow2_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let ten = Immediate::U32(10); - - emitter.literal(ten); - - emitter.pow2(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_incr_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let ten = Immediate::U32(10); - - emitter.literal(ten); - - emitter.incr(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_inv_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let ten = Immediate::Felt(Felt::new(10)); - - emitter.literal(ten); - - emitter.inv(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::Felt); - } - - #[test] - fn op_emitter_neg_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let ten = Immediate::Felt(Felt::new(10)); - - emitter.literal(ten); - - emitter.neg(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::Felt); - } - - #[test] - fn op_emitter_u32_assert_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let ten = Immediate::U32(10); - - emitter.literal(ten); - assert_eq!(emitter.stack_len(), 1); - - emitter.assert(); - assert_eq!(emitter.stack_len(), 0); - } - - #[test] - fn op_emitter_u32_assertz_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let ten = Immediate::U32(10); - - emitter.literal(ten); - assert_eq!(emitter.stack_len(), 1); - - emitter.assertz(); - assert_eq!(emitter.stack_len(), 0); - } - - #[test] - fn op_emitter_u32_assert_eq_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let ten = Immediate::U32(10); - - emitter.literal(ten); - emitter.literal(ten); - emitter.literal(ten); - assert_eq!(emitter.stack_len(), 3); - - emitter.assert_eq_imm(ten); - assert_eq!(emitter.stack_len(), 2); - - emitter.assert_eq(); - assert_eq!(emitter.stack_len(), 0); - } - - #[test] - fn op_emitter_u32_select_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let t = Immediate::I1(true); - let one = Immediate::U32(1); - let two = Immediate::U32(2); - - emitter.literal(one); - emitter.literal(two); - emitter.literal(t); - assert_eq!(emitter.stack_len(), 3); - - emitter.select(); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - } - - #[test] - fn op_emitter_u32_exec_test() { - use miden_hir::ExternalFunction; - - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let return_ty = Type::Array(Box::new(Type::U32), 1); - let callee = ExternalFunction { - id: "test::add".parse().unwrap(), - signature: Signature::new( - [AbiParam::new(Type::U32), AbiParam::new(Type::I1)], - [AbiParam::new(return_ty.clone())], - ), - }; - - let t = Immediate::I1(true); - let one = Immediate::U32(1); - - emitter.literal(t); - emitter.literal(one); - assert_eq!(emitter.stack_len(), 2); - - emitter.exec(&callee); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], return_ty); - } - - #[test] - fn op_emitter_u32_load_test() { - let mut function = setup(); - let entry = function.body.id(); - let mut stack = OperandStack::default(); - let mut emitter = OpEmitter::new(&mut function, entry, &mut stack); - - let addr = Type::Ptr(Box::new(Type::U8)); - - emitter.push(addr); - assert_eq!(emitter.stack_len(), 1); - - emitter.load(Type::U32); - assert_eq!(emitter.stack_len(), 1); - assert_eq!(emitter.stack()[0], Type::U32); - - emitter.load_imm(128, Type::I32); - assert_eq!(emitter.stack_len(), 2); - assert_eq!(emitter.stack()[0], Type::I32); - assert_eq!(emitter.stack()[1], Type::U32); - } - - #[inline] - fn setup() -> Function { - Function::new( - "test::test".parse().unwrap(), - Signature::new([AbiParam::new(Type::U32)], [AbiParam::new(Type::U32)]), - ) - } -} diff --git a/codegen/masm/src/codegen/emit/primop.rs b/codegen/masm/src/codegen/emit/primop.rs deleted file mode 100644 index ae1794956..000000000 --- a/codegen/masm/src/codegen/emit/primop.rs +++ /dev/null @@ -1,277 +0,0 @@ -use miden_hir::{self as hir, ArgumentExtension, ArgumentPurpose, Felt, Immediate, Type}; - -use crate::masm::Op; - -use super::{int64, OpEmitter}; - -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) { - let arg = self.stack.pop().expect("operand stack is empty"); - match arg.ty() { - Type::Felt - | Type::U32 - | Type::I32 - | Type::U16 - | Type::I16 - | Type::U8 - | Type::I8 - | Type::I1 => { - self.emit(Op::Assert); - } - Type::I128 => { - self.emit_all(&[Op::Assertz, Op::Assertz, Op::Assertz, Op::Assert]); - } - Type::U64 | Type::I64 => { - self.emit_all(&[Op::Assertz, Op::Assert]); - } - ty if !ty.is_integer() => { - panic!("invalid argument to assert: expected integer, got {ty}") - } - ty => unimplemented!("support for assert on {ty} is not implemented"), - } - } - - /// Assert that an integer value on the stack has the value 0 - /// - /// This operation consumes the input value. - pub fn assertz(&mut self) { - let arg = self.stack.pop().expect("operand stack is empty"); - match arg.ty() { - Type::Felt - | Type::U32 - | Type::I32 - | Type::U16 - | Type::I16 - | Type::U8 - | Type::I8 - | Type::I1 => { - self.emit(Op::Assertz); - } - ty @ (Type::I128 | Type::U64 | Type::I64) => { - self.emit_n(ty.size_in_bits() / 32, Op::Assertz); - } - ty if !ty.is_integer() => { - panic!("invalid argument to assertz: expected integer, got {ty}") - } - ty => unimplemented!("support for assertz on {ty} is not implemented"), - } - } - - /// 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) { - 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 assert_eq operands to have the same type" - ); - match ty { - Type::Felt - | Type::U32 - | Type::I32 - | Type::U16 - | Type::I16 - | Type::U8 - | Type::I8 - | Type::I1 => { - self.emit(Op::AssertEq); - } - Type::I128 => self.emit(Op::AssertEqw), - Type::U64 | Type::I64 => { - self.emit_all(&[ - // compare the hi bits - Op::Movup(2), - Op::AssertEq, - // compare the low bits - Op::AssertEq, - ]); - } - ty if !ty.is_integer() => { - panic!("invalid argument to assert_eq: expected integer, got {ty}") - } - ty => unimplemented!("support for assert_eq on {ty} is not implemented"), - } - } - - /// Emit code to assert that an integer value on the stack has the same value - /// as the provided immediate. - /// - /// This operation consumes the input value. - pub fn assert_eq_imm(&mut self, imm: Immediate) { - 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" - ); - match ty { - Type::Felt - | Type::U32 - | Type::I32 - | Type::U16 - | Type::I16 - | Type::U8 - | Type::I8 - | Type::I1 => { - self.emit_all(&[Op::EqImm(imm.as_felt().unwrap()), Op::Assert]); - } - Type::I128 => { - self.push_immediate(imm); - self.emit(Op::AssertEqw) - } - Type::I64 | Type::U64 => { - let imm = match imm { - Immediate::I64(i) => i as u64, - Immediate::U64(i) => i, - _ => 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, - ]) - } - ty if !ty.is_integer() => { - panic!("invalid argument to assert_eq: expected integer, got {ty}") - } - ty => unimplemented!("support for assert_eq on {ty} is not implemented"), - } - } - - /// Emit code to select between two values of the same type, based on a boolean condition. - /// - /// The semantics of this instruction are basically the same as Miden's `cdrop` instruction, - /// but with support for selecting between any of the representable integer/pointer types as values. - /// Given three values on the operand stack (in order of appearance), `c`, `b`, and `a`: - /// - /// * Pop `c` from the stack. This value must be an i1/boolean, or execution will trap. - /// * 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) { - 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"); - assert_eq!(c.ty(), Type::I1, "expected selector operand to be an i1"); - let ty = a.ty(); - assert_eq!(ty, b.ty(), "expected selections to be of the same type"); - match &ty { - Type::Felt - | Type::U32 - | Type::I32 - | Type::U16 - | Type::I16 - | Type::U8 - | Type::I8 - | Type::I1 => self.emit(Op::Cdrop), - Type::I128 => self.emit(Op::Cdropw), - 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] - ]); - } - ty if !ty.is_integer() => { - panic!("invalid argument to assert_eq: expected integer, got {ty}") - } - ty => unimplemented!("support for assert_eq on {ty} is not implemented"), - } - self.stack.push(ty); - } - - /// 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) { - let import = callee; - let callee = import.id; - let signature = &import.signature; - for i in 0..signature.arity() { - let param = &signature.params[i]; - let arg = self.stack.pop().expect("operand stack is empty"); - let ty = arg.ty(); - // Validate the purpose matches - match param.purpose { - ArgumentPurpose::StructReturn => { - assert_eq!(i, 0, "invalid function signature: sret parameters must be the first parameter, and only one sret parameter is allowed"); - assert_eq!(signature.results.len(), 0, "invalid function signature: a function with sret parameters cannot also have results"); - assert!(ty.is_pointer(), "invalid exec to {callee}: invalid argument for sret parameter, expected {}, got {ty}", ¶m.ty); - } - ArgumentPurpose::Default => (), - } - // Validate that the argument type is valid for the parameter ABI - match param.extension { - // Types must match exactly - ArgumentExtension::None => { - assert_eq!(ty, param.ty, "invalid call to {callee}: invalid argument type for parameter at index {i}"); - } - // Caller can provide a smaller type which will be zero-extended to the expected type - // - // However, the argument must be an unsigned integer, and of smaller or equal size in order for the types to differ - ArgumentExtension::Zext if ty != param.ty => { - assert!(param.ty.is_unsigned_integer(), "invalid function signature: zero-extension is only valid for unsigned integer types"); - assert!(ty.is_unsigned_integer(), "invalid call to {callee}: invalid argument type for parameter at index {i}, expected unsigned integer type, got {ty}"); - let expected_size = param.ty.size_in_bits(); - let provided_size = param.ty.size_in_bits(); - assert!(provided_size <= expected_size, "invalid call to {callee}: invalid argument type for parameter at index {i}, expected integer width to be <= {expected_size} bits"); - // Zero-extend this argument - self.stack.push(arg); - self.zext(¶m.ty); - self.stack.drop(); - } - // Caller can provide a smaller type which will be sign-extended to the expected type - // - // However, the argument must be an integer which can fit in the range of the expected type - ArgumentExtension::Sext if ty != param.ty => { - assert!(param.ty.is_signed_integer(), "invalid function signature: sign-extension is only valid for signed integer types"); - assert!(ty.is_integer(), "invalid call to {callee}: invalid argument type for parameter at index {i}, expected integer type, got {ty}"); - let expected_size = param.ty.size_in_bits(); - let provided_size = param.ty.size_in_bits(); - if ty.is_unsigned_integer() { - assert!(provided_size < expected_size, "invalid call to {callee}: invalid argument type for parameter at index {i}, expected unsigned integer width to be < {expected_size} bits"); - } else { - assert!(provided_size <= expected_size, "invalid call to {callee}: invalid argument type for parameter at index {i}, expected integer width to be <= {expected_size} bits"); - } - // Push the operand back on the stack for `sext` - self.stack.push(arg); - self.sext(¶m.ty); - self.stack.drop(); - } - ArgumentExtension::Zext | ArgumentExtension::Sext => (), - } - } - - for result in signature.results.iter() { - self.stack.push(result.ty.clone()); - } - - self.emit(Op::Exec(callee)); - } - - /// 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) { - todo!() - } -} diff --git a/codegen/masm/src/codegen/emit/smallint.rs b/codegen/masm/src/codegen/emit/smallint.rs deleted file mode 100644 index 87e75a732..000000000 --- a/codegen/masm/src/codegen/emit/smallint.rs +++ /dev/null @@ -1,330 +0,0 @@ -//! A "smallint" is an `N`-bit, signed or unsigned integer in two's complement representation, -//! where `N` is defined as being 32 bits or smaller, and a power of two. -//! -//! In Miden, unsigned smallint operations are largely handled with the native u32 operations, -//! however we perform range checks on checked operations to ensure the value is still a valid -//! `N`-bit integer. -//! -//! 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 crate::masm::Op; -use miden_hir::Overflow; - -use super::OpEmitter; - -#[allow(unused)] -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) { - // Use fallible conversion from u32 - self.try_int32_to_uint(n); - } - - /// 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); - } - - /// 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) { - 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)), - } - } - - /// 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) { - match n { - // i1 is always unsigned - 1 => (), - n => { - self.is_signed_smallint(n); - self.emit(Op::Assert); - } - } - } - - /// 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); - } - - /// Convert an unsigned N-bit integer to a field element - #[inline(always)] - pub fn uint_to_felt(&mut self, n: u32) { - // Conversion to felt is a no-op - assert_valid_integer_size!(n, 1, 32); - } - - /// Convert a signed N-bit integer to u64 - /// - /// 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)); - } - - /// Convert an unsigned N-bit integer to u64 - #[inline(always)] - pub fn uint_to_u64(&mut self, _: u32) { - self.emit(Op::PushU32(0)); - } - - /// Convert a signed N-bit integer to i128 - #[inline] - pub fn int_to_i128(&mut self, n: u32) { - self.sext_smallint(n, 128); - } - - /// Convert an unsigned N-bit integer to i128 - #[inline(always)] - pub fn uint_to_i128(&mut self, _n: u32) { - // zero-extend to i128 - self.emit_n(3, Op::PushU32(0)); - } - - /// 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) { - assert_valid_integer_size!(n, n, 256); - // No-op - if n == m { - return; - } - // The number of sign bits are the number of bits between N and 32 - let num_sign_bits = 32 - n; - // By subtracting 1 from 2^(32 - N), we get a value that is all 1s, - // shifting it left by N, and bitwise-OR'ing it with the input value, - // we produce the sign-extended value - let sign_bits = ((1 << num_sign_bits) - 1) << n; - // 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); - 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), - ]); - // Select the padding element value - self.select_int32(u32::MAX, 0); - // Pad out to M bits - self.pad_int32(m); - } else { - self.select_int32(sign_bits, 0); - // Sign-extend to i32 - self.emit(Op::U32Or); - } - } - - /// Zero-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. - #[inline] - pub fn zext_smallint(&mut self, n: u32, m: u32) { - assert_valid_integer_size!(n, n, 256); - // No-op - if n == m { - return; - } - self.zext_int32(m); - } - - pub fn add_uint(&mut self, n: u32, overflow: Overflow) { - match overflow { - Overflow::Unchecked => self.add_u32(Overflow::Unchecked), - overflow => { - self.add_u32(Overflow::Checked); - self.handle_uint_overflow(n, overflow) - } - } - } - - pub fn add_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow) { - match overflow { - Overflow::Unchecked => self.add_imm_u32(imm, Overflow::Unchecked), - overflow => { - self.add_imm_u32(imm, Overflow::Checked); - self.handle_uint_overflow(n, overflow) - } - } - } - - /// 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) { - match overflow { - Overflow::Unchecked => self.sub_u32(overflow), - overflow => { - self.sub_u32(overflow); - self.handle_uint_overflow(n, overflow); - } - } - } - - /// Pops a u32 value off the stack, `a`, and performs `a - `. - /// - /// See the [Overflow] type for how overflow semantics can change the operation. - /// - /// Subtracting zero is a no-op. - #[inline] - pub fn sub_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow) { - if imm == 0 { - return; - } - match overflow { - Overflow::Unchecked => self.sub_imm_u32(imm, overflow), - overflow => { - self.sub_imm_u32(imm, overflow); - self.handle_uint_overflow(n, overflow); - } - } - } - - pub fn mul_uint(&mut self, n: u32, overflow: Overflow) { - match overflow { - Overflow::Unchecked => self.mul_u32(Overflow::Unchecked), - overflow => { - self.mul_u32(Overflow::Checked); - self.handle_uint_overflow(n, overflow) - } - } - } - - pub fn mul_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow) { - match overflow { - Overflow::Unchecked => self.mul_imm_u32(imm, Overflow::Unchecked), - overflow => { - self.mul_imm_u32(imm, Overflow::Checked); - self.handle_uint_overflow(n, overflow) - } - } - } - - #[inline] - pub fn checked_div_uint(&mut self, n: u32) { - self.checked_div_u32(); - self.int32_to_uint(n); - } - - #[inline] - pub fn checked_div_imm_uint(&mut self, imm: u32, n: u32) { - self.checked_div_imm_u32(imm); - self.int32_to_uint(n); - } - - #[inline(always)] - pub fn unchecked_div_uint(&mut self, _n: u32) { - self.unchecked_div_u32(); - } - - #[inline(always)] - pub fn unchecked_div_imm_uint(&mut self, imm: u32, _n: u32) { - self.unchecked_div_imm_u32(imm); - } - - /// 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(); - } - - /// Pops a u32 value off the stack, `a`, and performs `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. - #[inline(always)] - pub fn checked_mod_imm_uint(&mut self, imm: u32, _n: u32) { - self.checked_mod_imm_u32(imm); - } - - /// 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(); - } - - /// 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); - } - - /// 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. - #[inline(always)] - pub fn checked_divmod_uint(&mut self, _n: u32) { - self.checked_divmod_u32(); - } - - /// 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); - } - - /// 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 - #[inline(always)] - pub fn unchecked_divmod_uint(&mut self, _n: u32) { - self.unchecked_divmod_u32(); - } - - /// 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 handle_uint_overflow(&mut self, n: u32, overflow: Overflow) { - match overflow { - Overflow::Unchecked => (), - Overflow::Checked => self.int32_to_uint(n), - Overflow::Wrapping => self.emit(Op::U32ModImm(2u32.pow(n))), - 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, - ]); - } - } - } -} diff --git a/codegen/masm/src/codegen/emit/unary.rs b/codegen/masm/src/codegen/emit/unary.rs deleted file mode 100644 index c3ec0c2a6..000000000 --- a/codegen/masm/src/codegen/emit/unary.rs +++ /dev/null @@ -1,577 +0,0 @@ -use miden_hir::{Overflow, Type}; - -use crate::masm::Op; - -use super::*; - -impl<'a> OpEmitter<'a> { - /// Truncate an integral value of type `src` to type `dst` - /// - /// Truncation is defined in terms of the bitwise representation of the type. - /// Specifically, the source value will have any excess bits above the bitwidth of - /// the `dst` type either zeroed, or dropped, depending on the `src` type. For example, - /// u64 is represented as two 32-bit limbs, each in a field element on the operand stack; - /// while u16 is represented as 16 bits in a single field element. Truncating from u64 - /// to u16 results in dropping the 32-bit limb containing the most significant bits, and - /// then masking out the most significant 16 bits of the remaining 32-bit limb, leaving - /// a 16-bit value on the operand stack. - /// - /// NOTE: Field elements do not have a formal bitwise representation. When truncating to - /// felt and the source value is negative, the resulting felt will be `Felt::ZERO`. When - /// the value is non-negative, the source value will be mapped to the field element range - /// using the field modulus of `2^64 - 2^32 + 1`, and then convert the representation to - /// felt by multiplying the 32-bit limbs (the only values which can be truncated to felt - /// are u64, i64, and i128, all of which use multiple 32-bit limbs). - /// - /// 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) { - let arg = self.stack.pop().expect("operand stack is empty"); - let src = arg.ty(); - assert!( - src.is_integer() && dst.is_integer(), - "invalid truncation of {src} to {dst}: only integer-to-integer casts are supported" - ); - let n = dst.size_in_bits() as u32; - 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), - // Truncating to felt - (Type::I128, Type::Felt) => self.trunc_i128_to_felt(), - // Truncating an i128 to 64 bits or smaller - (Type::I128, _) if n <= 64 => self.trunc_i128(n), - // Truncating i64/u64 to felt - (Type::I64 | Type::U64, Type::Felt) => self.trunc_int64_to_felt(), - // Truncating a u64/i64 to 32 bits or smaller - (Type::I64 | Type::U64, _) if n <= 32 => self.trunc_int64(n), - // Truncating a felt to 32 bits or smaller - (Type::Felt, _) if n <= 32 => self.trunc_felt(n), - // Truncating an i32/u32 to smaller than 32 bits - (Type::I32 | Type::U32, _) if n <= 32 => self.trunc_int32(n), - // Truncating an i16/u16 to smaller than 16 bits - (Type::I16 | Type::U16, _) if n <= 16 => self.trunc_int32(n), - // Truncating an i8/u8 to smaller than 8 bits - (Type::I8 | Type::U8, _) if n <= 8 => self.trunc_int32(n), - (src, dst) => unimplemented!("unsupported truncation of {src} to {dst}"), - } - self.stack.push(dst.clone()); - } - - /// Zero-extend an unsigned integral value of type `src` to type `dst` - /// - /// This function will panic if the source or target types are not unsigned integral types. - /// Despite its type name, i1 is an unsigned value, because it may only represent 1 or 0. - /// - /// Zero-extension is defined in terms of the bitwise representation of the type. - /// Specifically, the source value will have any excess bits above the bitwidth of - /// the `src` type either added as zeroes, or set to zero, depending on the `dst` type. - /// For example, u16 is represented as 16 bits in a single field element, while u64 is - /// represented as two 32-bit limbs, each in a separate field element. Zero-extending - /// from u16 to u64 requires only pushing a new element of `Felt::ZERO` on the operand stack. - /// Since the upper 16 bits of the original 32-bit field element value must already be zero, - /// we only needed to pad out the representation with an extra zero element to obtain the - /// corresponding u64. - /// - /// NOTE: Field elements do not have a formal bitwise representation. However, types with a - /// bitwidth of 32 bits or smaller are transparently represented as field elements in the VM, - /// so zero-extending to felt from such a type is a no-op. Even though a field element is - /// notionally a 64-bit value in memory, it is not equivalent in range to a 64-bit integer, - /// so 64-bit integers and above require the use of multiple 32-bit limbs, to provide a two's - /// complement bitwise representation; so there are no types larger than 32-bits that are - /// zero-extendable to felt, but are not representable as a felt transparently. - /// - /// 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) { - let arg = self.stack.pop().expect("operand stack is empty"); - let src = arg.ty(); - assert!( - src.is_unsigned_integer() && dst.is_integer(), - "invalid zero-extension of {src} to {dst}: only unsigned integer-to-integer casts are supported" - ); - let src_bits = src.size_in_bits() as u32; - let dst_bits = dst.size_in_bits() as u32; - assert!( - src_bits <= dst_bits, - "invalid zero-extension from {src} to {dst}: cannot zero-extend to a smaller type" - ); - match (&src, dst) { - // 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::I128) => self.push_u64(0), - (Type::Felt, Type::U64 | Type::I128) => self.zext_felt(dst_bits), - (Type::U32, Type::U64 | Type::I64 | Type::I128) => self.zext_int32(dst_bits), - (Type::I1 | Type::U8 | Type::U16, Type::U64 | Type::I64 | Type::I128) => self.zext_smallint(src_bits, dst_bits), - // Zero-extending to u32/i32 from smaller integers is a no-op - (Type::I1 | Type::U8 | Type::U16, Type::U32 | Type::I32) => (), - // Zero-extending to felt, from types that fit in felt, is a no-op - (Type::I1 | Type::U8 | Type::U16 | Type::U32, Type::Felt) => (), - (src, dst) if dst.is_signed_integer() => panic!("invalid zero-extension from {src} to {dst}: value may not fit in range, use explicit cast instead"), - (src, dst) => panic!("unsupported zero-extension from {src} to {dst}"), - } - self.stack.push(dst.clone()); - } - - /// Sign-extend an integral value of type `src` to type `dst` - /// - /// This function will panic if the target type is not a signed integral type. - /// To extend unsigned integer types to a larger unsigned integer type, use `zext`. - /// To extend signed integer types to an equal or larger unsigned type, use `cast`. - /// - /// Sign-extension is defined in terms of the bitwise representation of the type. - /// Specifically, the sign bit of the source value will be propagated to all excess - /// bits added to the representation of `src` to represent `dst`. - /// - /// For example, i16 is represented as 16 bits in a single field element, while i64 is - /// represented as two 32-bit limbs, each in a separate field element. Sign-extending - /// the i16 value -128, to i64, requires propagating the sign bit value, 1 since -128 - /// is a negative number, to the most significant 32-bits of the input element, as well - /// as pushing an additional element representing `u32::MAX` on the operand stack. This - /// gives us a bitwise representation where the most significant 48 bits are all 1s, and - /// the last 16 bits are the same as the original input value, giving us the i64 - /// representation of -128. - /// - /// NOTE: Field elements cannot be sign-extended to i64, you must an explicit cast, as the - /// range of the field means that it is not guaranteed that the felt will fit in the i64 - /// range. - /// - /// 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) { - let arg = self.stack.pop().expect("operand stack is empty"); - let src = arg.ty(); - assert!(src.is_integer() && dst.is_signed_integer(), "invalid sign-extension of {src} to {dst}: only integer-to-signed-integer casts are supported"); - let src_bits = src.size_in_bits() as u32; - let dst_bits = dst.size_in_bits() as u32; - assert!( - src_bits <= dst_bits, - "invalid zero-extension from {src} to {dst}: cannot zero-extend to a smaller type" - ); - 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::I1 | Type::I8 | Type::U8 | Type::I16 | Type::U16, - Type::I32 | Type::I64 | Type::I128, - ) => self.sext_smallint(src_bits, dst_bits), - (src, dst) => panic!("unsupported sign-extension from {src} to {dst}"), - } - self.stack.push(dst.clone()); - } - - /// Convert between two integral types, given as `src` and `dst`, - /// indicating the direction of the conversion. - /// - /// This function will panic if either type is not an integer. - /// - /// The specific semantics of a cast are dependent on the pair of types involved, - /// but the general rules are as follows: - /// - /// * Any integer-to-integer cast is allowed - /// * Casting a signed integer to an unsigned integer will assert that the - /// input value is unsigned - /// * Casting a type with a larger range to a type with a smaller one will - /// assert that the input value fits within that range, e.g. u8 to i8, i16 to i8, etc. - /// * Casting to a larger unsigned type will use zero-extension - /// * Casting a signed type to a larger signed type will use sign-extension - /// * Casting an unsigned type to a larger signed type will use zero-extension - /// - /// As a rule, the input value must be representable in the target type, or an - /// assertion will be raised. Casts are intended to faithfully translate a value - /// in one type to the same value in another type. - /// - /// 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) { - let arg = self.stack.pop().expect("operand stack is empty"); - let src = arg.ty(); - assert!( - src.is_integer() && dst.is_integer(), - "invalid cast of {src} to {dst}: only integer-to-integer casts are supported" - ); - - let src_bits = src.size_in_bits() as u32; - let dst_bits = dst.size_in_bits() as u32; - match (&src, dst) { - // i128 - (Type::I128, Type::I64) => self.i128_to_i64(), - (Type::I128, Type::U64) => self.i128_to_u64(), - (Type::I128, Type::Felt) => self.i128_to_felt(), - (Type::I128, Type::U32 | Type::U16 | Type::U8 | Type::I1) => { - self.i128_to_u64(); - self.u64_to_uint(dst_bits); - } - (Type::I128, Type::I32 | Type::I16 | Type::I8) => { - self.i128_to_i64(); - self.i64_to_int(dst_bits); - } - // i64 - (Type::I64, Type::I128) => self.sext_int64(128), - (Type::I64, Type::U64) => self.assert_unsigned_int64(), - (Type::I64, Type::Felt) => self.i64_to_felt(), - (Type::I64, Type::U32 | Type::U16 | Type::U8 | Type::I1) => { - self.assert_unsigned_int64(); - self.u64_to_uint(dst_bits); - } - (Type::I64, Type::I32 | Type::I16 | Type::I8) => { - self.i64_to_int(dst_bits); - } - // u64 - (Type::U64, Type::I64) => self.assert_i64(), - (Type::U64, Type::Felt) => self.u64_to_felt(), - (Type::U64, Type::U32 | Type::U16 | Type::U8 | Type::I1) => { - self.u64_to_uint(dst_bits); - } - (Type::U64, Type::I32 | Type::I16 | Type::I8) => { - // Convert to N bits as unsigned - self.u64_to_uint(dst_bits); - // Verify that the input value is still unsigned - self.assert_unsigned_smallint(dst_bits); - } - // felt - (Type::Felt, Type::I64 | Type::I128) => self.sext_felt(dst_bits), - (Type::Felt, Type::U64) => self.felt_to_u64(), - (Type::Felt, Type::U32 | Type::U16 | Type::U8 | Type::I1) => { - self.felt_to_uint(dst_bits); - } - (Type::Felt, Type::I32 | Type::I16 | Type::I8) => { - self.felt_to_int(dst_bits); - } - // u32 - (Type::U32, Type::I64 | Type::U64 | Type::I128) => self.zext_int32(dst_bits), - (Type::U32, Type::I32) => self.assert_unsigned_int32(), - (Type::U32, Type::U16 | Type::U8 | Type::I1) => { - self.int32_to_uint(dst_bits); - } - (Type::U32, Type::I16 | Type::I8) => self.int32_to_int(dst_bits), - // i32 - (Type::I32, Type::I64 | Type::I128) => self.sext_int32(dst_bits), - (Type::I32, Type::U64) => { - self.assert_unsigned_int32(); - self.emit(Op::PushU32(0)); - } - (Type::I32, Type::U32) => { - self.assert_unsigned_int32(); - } - (Type::I32, Type::U16 | Type::U8 | Type::I1) => { - self.int32_to_uint(dst_bits); - } - (Type::I32, Type::I16 | Type::I8) => self.int32_to_int(dst_bits), - // i8/i16 - (Type::I8 | Type::I16, Type::I32 | Type::I64 | Type::I128) => { - self.sext_smallint(src_bits, dst_bits); - } - (Type::I8 | Type::I16, Type::U32 | Type::U64) => { - self.assert_unsigned_smallint(src_bits); - self.zext_smallint(src_bits, dst_bits); - } - (Type::I16, Type::U16) | (Type::I8, Type::U8) => { - self.assert_unsigned_smallint(src_bits); - } - (Type::I16, Type::U8 | Type::I1) => self.int32_to_int(dst_bits), - (Type::I16, Type::I8) => self.int32_to_int(dst_bits), - (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, - ]); - } - // i1 - (Type::I1, _) => self.zext_smallint(src_bits, dst_bits), - (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) { - 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"); - match arg.ty() { - Type::U32 => { - self.stack.push(ty.clone()); - } - Type::Felt => { - self.emit(Op::U32Assert); - self.stack.push(ty.clone()); - } - int => panic!("invalid inttoptr cast: cannot cast value of type {int} to {ty}"), - } - } - - /// Check if an integral value on the operand stack is an odd number. - /// - /// The result is placed on the stack as a boolean value. - /// - /// This operation consumes the input operand. - pub fn is_odd(&mut self) { - let arg = self.stack.pop().expect("operand stack is empty"); - match arg.ty() { - // For both signed and unsigned types, - // values <= bitwidth of a felt can use - // the native instruction because the sign - // bit does not change whether the value is - // odd or not - Type::I1 - | Type::U8 - | Type::I8 - | Type::U16 - | Type::I16 - | Type::U32 - | Type::I32 - | Type::Felt => { - self.emit(Op::IsOdd); - } - // 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]); - } - // For i128, same as above, but more elements are dropped - Type::I128 => { - self.emit_n(3, Op::Drop); - self.emit(Op::IsOdd); - } - Type::F64 => { - unimplemented!("is_odd support for floating-point values is not yet implemented") - } - ty => panic!("expected integral type for is_odd opcode, got {ty}"), - } - self.stack.push(Type::I1); - } - - /// Count the number of non-zero bits in the integral value on top of the operand stack, - /// and place the count back on the stack as a u32 value. - /// - /// This operation consumes the input operand. - pub fn popcnt(&mut self) { - let arg = self.stack.pop().expect("operand stack is empty"); - let ty = arg.ty(); - match &ty { - Type::I128 => { - 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, - ]); - } - 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, - ]); - } - Type::I32 | Type::U32 | Type::I16 | Type::U16 | Type::I8 | Type::U8 | Type::I1 => { - self.emit(Op::U32Popcnt); - } - ty if !ty.is_integer() => { - panic!("invalid popcnt on {ty}: only integral types can be negated") - } - ty => unimplemented!("popcnt for {ty} is not supported"), - } - self.stack.push(ty); - } - - /// Invert the bitwise representation of the integral value on top of the operand stack. - /// - /// 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) { - let arg = self.stack.pop().expect("operand stack is empty"); - let ty = arg.ty(); - match &ty { - Type::I1 => self.emit(Op::Not), - Type::I8 - | Type::U8 - | Type::I16 - | Type::U16 - | Type::I32 - | Type::U32 - | Type::I64 - | Type::U64 - | Type::I128 => { - let num_elements = ty.size_in_bits() / 32; - match num_elements { - 0 | 1 => { - self.emit(Op::U32Not); - } - 2 => { - self.emit_repeat(2, &[Op::Swap(1), Op::U32Not]); - } - n => { - self.emit_template(n, |n| [Op::Movup(n as u8), Op::U32Not]); - } - } - } - ty if !ty.is_integer() => { - panic!("invalid bnot on {ty}, only integral types are supported") - } - ty => unimplemented!("bnot for {ty} is not supported"), - } - self.stack.push(ty); - } - - /// Invert the boolean value on top of the operand stack. - /// - /// This operation consumes the input operand. - pub fn not(&mut self) { - 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.stack.push(Type::I1); - } - - /// Compute 2^N, where N is the integral value on top of the operand stack, as - /// a value of the same type as the input. - /// - /// The input value must be < 64, or execution will trap. - /// - /// This operation consumes the input operand. - pub fn pow2(&mut self) { - 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, - ]); - } - Type::Felt => { - self.emit(Op::Pow2); - } - Type::U32 => { - self.emit_all(&[Op::Pow2, Op::U32Assert]); - } - Type::I32 => { - self.emit_all(&[Op::Exec("intrinsics::i32::pow2".parse().unwrap())]); - } - Type::U8 | Type::U16 => { - self.emit_all(&[Op::Pow2, Op::U32Assert]); - // Cast u32 to u8/u16 - self.int32_to_uint(ty.size_in_bits() as u32); - } - ty if !ty.is_unsigned_integer() => { - panic!( - "invalid unary operand: pow2 only permits unsigned integer operands, got {ty}" - ) - } - ty => unimplemented!("pow2 for {ty} is not supported"), - } - self.stack.push(ty); - } - - /// Increment the operand on top of the stack by 1. - /// - /// The input value must be an integer, and overflow has wrapping semantics. - /// - /// This operation consumes the input operand. - pub fn incr(&mut self) { - 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); - } - Type::Felt => { - self.emit(Op::Incr); - } - // For this specific case, wrapping u32 arithmetic works for both i32/u32 - Type::I32 | Type::U32 => { - self.add_imm_u32(1, Overflow::Wrapping); - } - // 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)); - } - ty if !ty.is_integer() => { - panic!("invalid unary operand: incr requires an integer operand, got {ty}") - } - ty => unimplemented!("incr for {ty} is not supported"), - } - self.stack.push(ty); - } - - /// Compute the modular multiplicative inverse of the operand on top of the stack, `n`, i.e. `n^-1 mod P`. - /// - /// This operation consumes the input operand. - pub fn inv(&mut self) { - let arg = self.pop().expect("operand stack is empty"); - let ty = arg.ty(); - match &ty { - Type::Felt => { - self.emit(Op::Inv); - } - ty if !ty.is_integer() => { - panic!("invalid unary operand: inv requires an integer, got {ty}") - } - ty => unimplemented!("inv for {ty} is not supported"), - } - self.push(ty); - } - - /// 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) { - let arg = self.pop().expect("operand stack is empty"); - let ty = arg.ty(); - match &ty { - Type::Felt => { - self.emit(Op::Neg); - } - ty if !ty.is_integer() => { - panic!("invalid unary operand: neg requires an integer, got {ty}") - } - ty => unimplemented!("neg for {ty} is not supported"), - } - self.push(ty); - } -} diff --git a/codegen/masm/src/codegen/emitter.rs b/codegen/masm/src/codegen/emitter.rs deleted file mode 100644 index 1340b7fe9..000000000 --- a/codegen/masm/src/codegen/emitter.rs +++ /dev/null @@ -1,1112 +0,0 @@ -use std::rc::Rc; - -use cranelift_entity::SecondaryMap; -use miden_hir::{self as hir, adt::SparseMap, assert_matches}; -use miden_hir_analysis::{ - DominatorTree, GlobalVariableLayout, LivenessAnalysis, Loop, LoopAnalysis, -}; -use smallvec::SmallVec; - -use crate::masm::{self, Op}; - -use super::{ - emit::{InstOpEmitter, OpEmitter}, - opt::{OperandMovementConstraintSolver, SolverError}, - scheduler::{BlockInfo, InstInfo, Schedule, ScheduleOp}, - Constraint, OperandStack, -}; - -pub struct FunctionEmitter<'a> { - f: &'a hir::Function, - f_prime: &'a mut masm::Function, - domtree: &'a DominatorTree, - loops: &'a LoopAnalysis, - liveness: &'a LivenessAnalysis, - globals: &'a GlobalVariableLayout, - visited: SecondaryMap, -} - -struct BlockEmitter<'b, 'f: 'b> { - function: &'b mut FunctionEmitter<'f>, - block_infos: &'b SparseMap>, - block_info: Rc, - /// The "controlling" loop corresponds to the current maximum loop depth - /// reached along the control flow path reaching this block. When we reach - /// a loopback edge in the control flow graph, we emit a trailing duplicate of the - /// instructions in the loop header to which we are branching. The controlling - /// loop is used during this process to determine what action, if any, must - /// be taken to exit the current loop in order to reach the target loop. - /// - /// Because we expect the IR we process to have been treeified, each block - /// can only have a single controlling loop, or none. This is because the only - /// blocks with multiple predecessors are loop headers, where the additional - /// predecessors must be loopback edges. Since a loopback edge does not modify - /// the controlling loop of the loop header block, it can only have a single - /// controlling loop. - controlling_loop: Option, - stack: OperandStack, - target: masm::BlockId, - visited: bool, -} - -/// Represents a task to execute during function emission -#[derive(Debug)] -enum Task { - /// Emit a block into a fresh block - Block { - /// The block to emit - block: hir::Block, - /// If set, the given loop should be used as the controlling - /// loop when determining what loop level is being exited - /// from. - /// - /// This gets set on any blocks emitted along a loopback edge - /// in the control flow graph, and is otherwise None. - controlling_loop: Option, - /// The state of the operand stack at block entry - stack: OperandStack, - }, - /// Emit a block by appending it to an existing block - Inline { - /// The block to inline into - target: masm::BlockId, - /// The block to emit - block: hir::Block, - /// If set, the given loop should be used as the controlling - /// loop when determining what loop level is being exited - /// from. - /// - /// This gets set on any blocks emitted along a loopback edge - /// in the control flow graph, and is otherwise None. - controlling_loop: Option, - /// The state of the operand stack at block entry - stack: OperandStack, - }, -} - -/// The task stack used during function emission -type Tasks = SmallVec<[Task; 4]>; - -impl<'a> FunctionEmitter<'a> { - pub fn new( - f: &'a hir::Function, - f_prime: &'a mut masm::Function, - domtree: &'a DominatorTree, - loops: &'a LoopAnalysis, - liveness: &'a LivenessAnalysis, - globals: &'a GlobalVariableLayout, - ) -> Self { - Self { - f, - f_prime, - domtree, - loops, - liveness, - globals, - visited: SecondaryMap::new(), - } - } - - pub fn emit(mut self, schedule: Schedule, stack: OperandStack) { - let mut tasks = Tasks::from_iter([Task::Block { - block: self.f.dfg.entry_block(), - controlling_loop: None, - stack, - }]); - while let Some(task) = tasks.pop() { - match task { - Task::Block { - block: block_id, - controlling_loop, - stack, - } => { - let block_info = schedule.block_info(block_id); - let target = block_info.target; - let block_schedule = schedule.get(block_id); - let visited = core::mem::replace(&mut self.visited[block_id], true); - let emitter = BlockEmitter { - function: &mut self, - block_infos: &schedule.block_infos, - block_info, - controlling_loop, - target, - stack, - visited, - }; - emitter.emit(block_schedule, &mut tasks); - } - Task::Inline { - target, - block: block_id, - controlling_loop, - stack, - } => { - let block_info = schedule.block_info(block_id); - let block_schedule = schedule.get(block_id); - let visited = core::mem::replace(&mut self.visited[block_id], true); - let emitter = BlockEmitter { - function: &mut self, - block_infos: &schedule.block_infos, - block_info, - controlling_loop, - target, - stack, - visited, - }; - emitter.emit(block_schedule, &mut tasks); - } - } - } - } -} - -impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { - pub fn emit(mut self, block_schedule: &[ScheduleOp], tasks: &mut Tasks) { - // Before we emit any scheduling operations, compare the current stack - // against the set of live-in values expected by this block. If there are - // any values on the stack which are not live-in, then they should be dropped - // here. - // - // This situation occurs when the scheduler deems that a value must - // be copied for all uses in a given block because it is live in at least one - // successor, causing the value to be kept on the stack when transferring control - // to any successor of that block. When this interacts with conditional branches, - // where the value is only used in a subset of successors, there will be at least - // one successor where the value is left dangling and essentially never cleaned - // up. This causes issues with operand stack coherence in loops. We can't avoid - // making the copy in the original block, instead responsibility for cleaning - // up these unused values is pushed into the successor on entry. - self.drop_unused_operands(); - - // 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(); - let pos = emitter - .stack() - .find(value) - .expect("could not find value on the operand stack"); - emitter.drop_operand_at_position(pos); - } - } - } - } - - fn emit_inst(&mut self, inst_info: &InstInfo, tasks: &mut Tasks) { - use miden_hir::Instruction; - - // Move instruction operands into place, minimizing unnecessary stack manipulation ops - // - // 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 for {}: {err:?}", - inst_info.inst - ) - }); - - match self.function.f.dfg.inst(inst_info.inst) { - ix @ (Instruction::RetImm(_) | Instruction::Ret(_)) => self.emit_ret(inst_info, ix), - Instruction::Br(ref op) => self.emit_br(inst_info, op, tasks), - Instruction::CondBr(ref op) => self.emit_cond_br(inst_info, op, tasks), - Instruction::GlobalValue(op) => self.emit_global_value(inst_info, op), - Instruction::UnaryOpImm(op) => self.emit_unary_imm_op(inst_info, op), - Instruction::UnaryOp(op) => self.emit_unary_op(inst_info, op), - Instruction::BinaryOpImm(op) => self.emit_binary_imm_op(inst_info, op), - Instruction::BinaryOp(op) => self.emit_binary_op(inst_info, op), - Instruction::Test(op) => self.emit_test_op(inst_info, op), - Instruction::Load(op) => self.emit_load_op(inst_info, op), - Instruction::PrimOp(op) => self.emit_primop(inst_info, op), - Instruction::PrimOpImm(op) => self.emit_primop_imm(inst_info, op), - Instruction::Call(op) => self.emit_call_op(inst_info, op), - Instruction::InlineAsm(op) => self.emit_inline_asm(inst_info, op), - Instruction::Switch(_) => { - panic!("expected switch instructions to have been rewritten before stackification") - } - } - } - - fn emit_ret(&mut self, inst_info: &InstInfo, ix: &hir::Instruction) { - use miden_hir::Instruction; - assert!( - !self.visited, - "invalid control flow graph: unexpected return instruction in loop in {}", - self.function.f.dfg.inst_block(inst_info.inst).unwrap(), - ); - - 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); - // 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); - } - - // 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); - } - } - - /// Lower an unconditional branch instruction. - /// - /// There are two ways in which code generation lowers these instructions, depending on - /// whether we have visited the successor block previously, nor not. - /// - /// * If this is the first visit to the successor, then due to the transformation passes - /// we expect to have been run on the input IR (namely treeification and block inlining), - /// it should be the case that these unconditional branches only exist in the IR when the - /// current block is a loop header, or the successor is a loop header. - /// - /// * If we have visited the successor previously, then we are emitting code for a loopback - /// edge, and the successor must be a loop header. We must emit the loop header inline in the - /// 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 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)) - .unwrap_or_else(|err| { - panic!( - "failed to schedule operands for {}: {err:?}", - inst_info.inst - ) - }); - // Rename operands on stack to destination block parameters - let params = self.function.f.dfg.block_params(destination); - for (idx, param) in params.iter().enumerate() { - self.stack.rename(idx, *param); - } - - if is_first_visit { - let controlling_loop = self.target_controlling_loop(destination); - 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)]); - tasks.push(Task::Block { - block: destination, - controlling_loop, - stack: self.stack.clone(), - }); - } else { - // We're in a normal block, emit the target block inline - tasks.push(Task::Inline { - target: self.target, - block: destination, - controlling_loop, - stack: self.stack.clone(), - }); - } - } else { - // We should only be emitting code for a block more than once if that block - // is a loop header. All other blocks should only be visited a single time. - assert!( - in_loop_header, - "unexpected cycle at {}", - self.block_info.source - ); - - // Calculate - let current_level = self.controlling_loop_level().unwrap_or_else(|| { - panic!( - "expected controlling loop to be set in {}", - self.block_info.source - ) - }); - let target_level = self.loop_level(self.block_info.source); - let mut emitter = self.emitter(); - emitter.literal(true); - for _ in 0..(current_level - target_level) { - emitter.literal(false); - } - } - } - - 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; - - // Ensure `cond` is on top of the stack, and remove it at the same time - assert_eq!( - self.stack.pop().unwrap().as_value(), - Some(cond), - "expected {} on top of the stack", - cond - ); - - if !self.visited { - let then_blk = self.masm_block_id(then_dest); - let else_blk = self.masm_block_id(else_dest); - - // If the current block is a loop header, we're emitting a conditional loop, - // otherwise we're emitting a simple if/else conditional expression. - 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)); - } else { - self.emit_op(Op::If(then_blk, else_blk)); - } - - let successors = [ - (then_dest, then_blk, op.then_dest.1), - (else_dest, else_blk, op.else_dest.1), - ]; - 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 - // successor block - let mut stack = self.stack.clone(); - - // Move block arguments for this successor into place, along - // the control flow edge to that successor, i.e. we do not emit - // these stack ops in the current block, but in the successor block - let args = args.as_slice(&self.function.f.dfg.value_lists); - self.schedule_operands_in_block( - args, - inst_info.block_arguments(block), - masm_block, - &mut stack, - ) - .unwrap_or_else(|err| { - panic!( - "failed to schedule operands for successor {block} of {}: {err:?}", - inst_info.inst - ) - }); - - // Now that the block arguments are in place, we need to rename - // the stack operands to use the value names the successor expects - let params = self.function.f.dfg.block_params(block); - for (idx, param) in params.iter().enumerate() { - stack.rename(idx, *param); - } - - // Enqueue a task to emit code for the successor block - let controlling_loop = self.target_controlling_loop(block); - tasks.push(Task::Block { - block, - controlling_loop, - stack, - }); - } - } else { - // We should only be emitting code for a block more than once if that block - // is a loop header. All other blocks should only be visited a single time. - assert!( - self.block_info.is_loop_header(), - "unexpected cycle caused by branch to {}", - self.block_info.source, - ); - - let current_level = self.controlling_loop_level().unwrap_or_else(|| { - panic!( - "expected controlling loop to be set in {}", - self.block_info.source - ) - }); - let target_level = self.loop_level(self.block_info.source); - // Continue the target loop when it is reached, the top of the stack - // 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); - for _ in 0..(current_level - target_level) { - emitter.literal(false); - } - } - } - - fn emit_global_value(&mut self, inst_info: &InstInfo, op: &hir::GlobalValueOp) { - assert_eq!(op.op, hir::Opcode::GlobalValue); - let addr = self - .function - .globals - .get_computed_addr(&self.function.f.id, op.global) - .expect("expected linker to identify all undefined symbols"); - 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()); - } - hir::GlobalValueData::IAddImm { .. } | hir::GlobalValueData::Symbol { .. } => { - let mut emitter = self.inst_emitter(inst_info.inst); - emitter.stack_mut().push(addr); - } - } - } - - fn emit_unary_imm_op(&mut self, inst_info: &InstInfo, op: &hir::UnaryOpImm) { - use miden_hir::Immediate; - - 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); - } - hir::Opcode::ImmI8 => { - assert_matches!(op.imm, Immediate::I8(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmU8 => { - assert_matches!(op.imm, Immediate::U8(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmI16 => { - assert_matches!(op.imm, Immediate::I16(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmU16 => { - assert_matches!(op.imm, Immediate::U16(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmI32 => { - assert_matches!(op.imm, Immediate::I32(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmU32 => { - assert_matches!(op.imm, Immediate::U32(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmI64 => { - assert_matches!(op.imm, Immediate::I64(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmU64 => { - assert_matches!(op.imm, Immediate::U64(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmFelt => { - assert_matches!(op.imm, Immediate::Felt(_)); - emitter.literal(op.imm); - } - hir::Opcode::ImmF64 => { - assert_matches!(op.imm, Immediate::F64(_)); - emitter.literal(op.imm); - } - opcode => unimplemented!("unrecognized unary with immediate opcode: '{opcode}'"), - } - } - - 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 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::Pow2 => emitter.pow2(), - hir::Opcode::Not => emitter.not(), - hir::Opcode::Bnot => emitter.bnot(), - hir::Opcode::Popcnt => emitter.popcnt(), - // This opcode is a no-op - hir::Opcode::PtrToInt => { - let result_ty = emitter.value_type(result).clone(); - let stack = emitter.stack_mut(); - stack.pop().expect("operand stack is empty"); - stack.push(result_ty); - } - // 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); - } - // 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); - } - hir::Opcode::Trunc => { - let dst_ty = emitter.value_type(result).clone(); - emitter.trunc(&dst_ty); - } - hir::Opcode::Zext => { - let dst_ty = emitter.value_type(result).clone(); - emitter.zext(&dst_ty); - } - hir::Opcode::Sext => { - let dst_ty = emitter.value_type(result).clone(); - emitter.sext(&dst_ty); - } - hir::Opcode::IsOdd => emitter.is_odd(), - opcode => unimplemented!("unrecognized unary opcode: '{opcode}'"), - } - } - - fn emit_binary_imm_op(&mut self, inst_info: &InstInfo, op: &hir::BinaryOpImm) { - use miden_hir::Overflow; - - 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), - opcode => unimplemented!("unrecognized binary with immediate opcode: '{opcode}'"), - } - } - - fn emit_binary_op(&mut self, inst_info: &InstInfo, op: &hir::BinaryOp) { - use miden_hir::Overflow; - - 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(), - opcode => unimplemented!("unrecognized binary opcode: '{opcode}'"), - } - } - - fn emit_test_op(&mut self, _inst_info: &InstInfo, op: &hir::Test) { - unimplemented!("unrecognized test opcode: '{}'", &op.op); - } - - fn emit_load_op(&mut self, inst_info: &InstInfo, op: &hir::LoadOp) { - let mut emitter = self.inst_emitter(inst_info.inst); - emitter.load(op.ty.clone()); - } - - fn emit_primop_imm(&mut self, inst_info: &InstInfo, op: &hir::PrimOpImm) { - let mut emitter = self.inst_emitter(inst_info.inst); - match op.op { - hir::Opcode::AssertEq => { - emitter.assert_eq_imm(op.imm); - } - // Store a value at a constant address - hir::Opcode::Store => { - emitter.store_imm( - op.imm - .as_u32() - .expect("invalid address immediate: out of range"), - ); - } - opcode => unimplemented!("unrecognized primop with immediate opcode: '{opcode}'"), - } - } - - fn emit_primop(&mut self, inst_info: &InstInfo, op: &hir::PrimOp) { - let args = op.args.as_slice(&self.function.f.dfg.value_lists); - 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(); - } - // 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(); - } - // Pop two values of the given type off the stack and assert equality - hir::Opcode::AssertEq => { - assert_eq!(args.len(), 2); - emitter.assert_eq(); - } - // 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); - } - // Store a value at a given pointer - hir::Opcode::Store => { - assert_eq!(args.len(), 2); - emitter.store(); - } - // Copy `count * sizeof(ctrl_ty)` bytes from source to destination address - hir::Opcode::MemCpy => { - assert_eq!(args.len(), 3); - emitter.memcpy(); - } - // Conditionally select between two values - hir::Opcode::Select => { - assert_eq!(args.len(), 3); - emitter.select(); - } - // 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]); - } - opcode => unimplemented!("unrecognized primop with immediate opcode: '{opcode}'"), - } - } - - fn emit_call_op(&mut self, inst_info: &InstInfo, op: &hir::Call) { - assert_ne!(op.callee, self.function.f.id, "unexpected recursive call"); - - 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), - opcode => unimplemented!("unrecognized procedure call opcode: '{opcode}'"), - } - } - - fn emit_inline_asm(&mut self, inst_info: &InstInfo, op: &hir::InlineAsm) { - use super::TypedValue; - - // Port over the blocks from the inline assembly chunk, except the body block, which will - // be inlined at the current block - let mut mapped = SecondaryMap::::new(); - for (inline_blk, _) in op.blocks.iter() { - if inline_blk == op.body { - continue; - } - let mapped_blk = self.function.f_prime.create_block(); - mapped[inline_blk] = mapped_blk; - } - - // Inline the body, rewriting any references to other blocks - let original_body_block = op.body; - let mapped_body_block = self.masm_block_id(self.block_info.source); - let mut rewrites = SmallVec::<[(masm::BlockId, masm::BlockId); 4]>::from_iter([( - original_body_block, - mapped_body_block, - )]); - self.rewrite_inline_assembly_block(op, &mut rewrites, &mapped); - - // Pop arguments, push results - self.stack - .dropn(op.args.len(&self.function.f.dfg.value_lists)); - for result in self - .function - .f - .dfg - .inst_results(inst_info.inst) - .iter() - .copied() - .rev() - { - let ty = self.function.f.dfg.value_type(result).clone(); - self.stack.push(TypedValue { value: result, ty }); - } - } - - fn rewrite_inline_assembly_block( - &mut self, - asm: &hir::InlineAsm, - rewrites: &mut SmallVec<[(masm::BlockId, masm::BlockId); 4]>, - mapped_blocks: &SecondaryMap, - ) { - while let Some((prev, new)) = rewrites.pop() { - for mut op in asm.blocks[prev].ops.iter().cloned() { - match op { - Op::If(ref mut then_blk, ref mut else_blk) => { - let prev_then_blk = *then_blk; - let prev_else_blk = *else_blk; - *then_blk = mapped_blocks[prev_then_blk]; - *else_blk = mapped_blocks[prev_else_blk]; - rewrites.push((prev_then_blk, *then_blk)); - rewrites.push((prev_else_blk, *else_blk)); - } - Op::While(ref mut body_blk) | Op::Repeat(_, ref mut body_blk) => { - let prev_body_blk = *body_blk; - *body_blk = mapped_blocks[prev_body_blk]; - rewrites.push((prev_body_blk, *body_blk)); - } - Op::LocAddr(_) | Op::LocStore(_) | Op::LocStorew(_) => { - unimplemented!( - "locals are not currently supported in inline assembly blocks" - ) - } - _ => (), - } - self.function.f_prime.body.block_mut(new).push(op); - } - } - } - - /// Drop the operands on the stack which are no longer live upon entry into - /// the current block. - /// - /// This is intended to be called before scheduling any instructions in the block. - fn drop_unused_operands(&mut self) { - // We start by computing the set of unused operands on the stack at this point - // in the program. We will use the resulting vectors to schedule instructions - // that will move those operands to the top of the stack to be discarded - let pp = hir::ProgramPoint::Block(self.block_info.source); - let mut unused = SmallVec::<[hir::Value; 4]>::default(); - let mut constraints = SmallVec::<[Constraint; 4]>::default(); - for operand in self.stack.iter().rev() { - let value = operand - .as_value() - .expect("unexpected non-ssa value on stack"); - // If the given value is not live on entry to this block, it should be dropped - if !self.function.liveness.is_live_at(&value, pp) { - println!( - "should drop {value} at {} (visited={})", - self.block_info.source, self.visited - ); - unused.push(value); - constraints.push(Constraint::Move); - } - } - - // Next, emit the optimal set of moves to get the unused operands to the top - if !unused.is_empty() { - // If the number of unused operands is greater than the number - // of used operands, then we will schedule manually, since this - // is a pathological use case for the operand scheduler. - let num_used = self.stack.len() - unused.len(); - if unused.len() > num_used { - // In this case, we emit code starting from the top - // of the stack, i.e. if we encounter an unused value - // on top, then we increment a counter and check the - // next value, and so on, until we reach a used value - // or the end of the stack. At that point, we emit drops - // for the unused batch, and reset the counter. - // - // If we encounter a used value on top, or we have dropped - // an unused batch and left a used value on top, we look - // to see if the next value is used/unused: - // - // * If used, we increment the counter until we reach an - // unused value or the end of the stack. We then move any - // unused value found to the top and drop it, subtract 1 - // from the counter, and resume where we left off - // - // * If unused, we check if it is just a single unused value, - // or if there is a string of unused values starting there. - // In the former case, we swap it to the top of the stack and - // drop it, and start over. In the latter case, we move the - // used value on top of the stack down past the last unused - // value, and then drop the unused batch. - let mut batch_size = 0; - let mut current_index = 0; - let mut unused_batch = false; - while self.stack.len() > current_index { - let value = self.stack[current_index].as_value().unwrap(); - let is_unused = unused.contains(&value); - // If we're looking at the top operand, start - // a new batch of either used or unused operands - if current_index == 0 { - unused_batch = is_unused; - current_index += 1; - batch_size += 1; - continue; - } - - // If we're putting together a batch of unused values, - // and the current value is unused, extend the batch - if unused_batch && is_unused { - batch_size += 1; - current_index += 1; - continue; - } - - // If we're putting together a batch of unused values, - // and the current value is used, drop the unused values - // 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); - batch_size = 0; - current_index = 0; - continue; - } - - // If we're putting together a batch of used values, - // and the current value is used, extend the batch - if !is_unused { - batch_size += 1; - current_index += 1; - continue; - } - - // Otherwise, we have found more unused value(s) behind - // a batch of used value(s), so we need to determine the - // best course of action - match batch_size { - // If we've only found a single used value so far, - // and there is more than two unused values behind it, - // then move the used value down the stack and drop the unused. - 1 => { - let unused_chunk_size = self - .stack - .iter() - .rev() - .skip(1) - .take_while(|o| unused.contains(&o.as_value().unwrap())) - .count(); - let mut emitter = self.emitter(); - if unused_chunk_size > 1 { - emitter.movdn(unused_chunk_size as u8); - emitter.dropn(unused_chunk_size); - } else { - emitter.swap(1); - emitter.drop(); - } - } - // 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(); - } - } - 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 - ) - }); - let mut emitter = self.emitter(); - emitter.dropn(unused.len()); - } - } - } - - fn schedule_operands( - &mut self, - expected: &[hir::Value], - constraints: &[Constraint], - ) -> Result<(), SolverError> { - match OperandMovementConstraintSolver::new(expected, constraints, &self.stack) { - Ok(solver) => { - let mut emitter = self.emitter(); - solver.solve_and_apply(&mut emitter) - } - Err(SolverError::AlreadySolved) => Ok(()), - Err(err) => { - panic!("unexpected error constructing operand movement constraint solver: {err:?}") - } - } - } - - fn schedule_operands_in_block( - &mut self, - expected: &[hir::Value], - constraints: &[Constraint], - block: masm::BlockId, - stack: &mut OperandStack, - ) -> 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) - } - Err(SolverError::AlreadySolved) => Ok(()), - Err(err) => { - panic!("unexpected error constructing operand movement constraint solver: {err:?}") - } - } - } - - fn target_controlling_loop(&self, target_block: hir::Block) -> Option { - use core::cmp::Ordering; - - let is_first_visit = !self.visited; - let current_block = self.block_info.source; - let current_loop = self.function.loops.innermost_loop(current_block); - let target_loop = self.function.loops.innermost_loop(target_block); - match (current_loop, target_loop) { - // No loops involved - (None, None) => { - assert!(is_first_visit); - assert_eq!(self.controlling_loop, None); - None - } - // Entering a top-level loop, set the controlling loop - (None, controlling_loop @ Some(_)) => { - assert!(is_first_visit); - assert_eq!(self.controlling_loop, None); - controlling_loop - } - // Escaping a loop - (Some(_), None) => { - assert!(is_first_visit); - // We're emitting a block along an exit edge of a loop, it must be the - // case here that the source block dominates the target block, so we - // leave the controlling loop alone, since it will be used to calculate - // the depth we're exiting from - assert!( - self.function.domtree.dominates( - current_block, - target_block, - &self.function.f.dfg - ), - "expected {current_block} to dominate {target_block} here" - ); - assert_matches!(self.controlling_loop, Some(_)); - self.controlling_loop - } - (Some(src), Some(dst)) => { - let src_level = self.function.loops.level(src); - let dst_level = self.function.loops.level(dst); - if is_first_visit { - // We have not visited the target block before.. - match src_level.cmp(&dst_level) { - // We're emitting a block along an exit edge of a loop, so we - // expect that the source block dominates the target block, and - // as such we will leave the controlling loop alone as it will - // be used to calculate the depth we're exiting to - Ordering::Greater => { - assert!( - self.function.domtree.dominates( - current_block, - target_block, - &self.function.f.dfg - ), - "expected {current_block} to dominate {target_block} here" - ); - self.controlling_loop - } - // If we're entering a nested loop, then we need to update the controlling loop - // to reflect the loop we've entered - Ordering::Less => Some(dst), - Ordering::Equal => self.controlling_loop, - } - } else { - // We're looping back to the loop header, or a parent loop header, - // so leave the controlling loop unmodified, it will be reset by - // the emit_inst handling - self.controlling_loop - } - } - } - } - - fn masm_block_id(&self, block: hir::Block) -> masm::BlockId { - self.block_infos.get(block).unwrap().target - } - - /// Get a mutable reference to the current block of code in the stack machine IR - #[inline(always)] - fn current_block(&mut self) -> &mut masm::Block { - self.function.f_prime.body.block_mut(self.target) - } - - /// Get a mutable reference to a specific block of code in the stack machine IR - #[inline(always)] - fn block(&mut self, block: masm::BlockId) -> &mut masm::Block { - self.function.f_prime.body.block_mut(block) - } - - #[inline] - fn emit_op(&mut self, op: Op) { - self.current_block().push(op); - } - - #[inline] - fn emit_op_to(&mut self, block: masm::BlockId, op: Op) { - self.block(block).push(op); - } - - #[inline] - fn emit_ops(&mut self, ops: impl IntoIterator) { - self.current_block().extend(ops); - } - - fn controlling_loop_level(&self) -> Option { - self.controlling_loop - .map(|lp| self.function.loops.level(lp).level()) - } - - fn loop_level(&self, block: hir::Block) -> usize { - self.function.loops.loop_level(block).level() - } - - #[inline(always)] - fn inst_emitter<'short, 'long: 'short>( - &'long mut self, - inst: hir::Inst, - ) -> InstOpEmitter<'short> { - InstOpEmitter::new( - self.function.f_prime, - &self.function.f.dfg, - inst, - self.target, - &mut self.stack, - ) - } - - #[inline(always)] - fn emitter<'short, 'long: 'short>(&'long mut self) -> OpEmitter<'short> { - OpEmitter::new(self.function.f_prime, self.target, &mut self.stack) - } -} diff --git a/codegen/masm/src/codegen/mod.rs b/codegen/masm/src/codegen/mod.rs deleted file mode 100644 index 65dc6037b..000000000 --- a/codegen/masm/src/codegen/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod emit; -mod emitter; -mod opt; -mod scheduler; -mod stack; - -pub use self::emitter::FunctionEmitter; -pub use self::scheduler::Scheduler; -pub use self::stack::{Constraint, Operand, OperandStack, TypedValue}; diff --git a/codegen/masm/src/codegen/opt/mod.rs b/codegen/masm/src/codegen/opt/mod.rs deleted file mode 100644 index 1c9d32297..000000000 --- a/codegen/masm/src/codegen/opt/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod operands; - -pub use self::operands::{OperandMovementConstraintSolver, SolverError}; diff --git a/codegen/masm/src/codegen/opt/operands/context.rs b/codegen/masm/src/codegen/opt/operands/context.rs deleted file mode 100644 index 429b7f802..000000000 --- a/codegen/masm/src/codegen/opt/operands/context.rs +++ /dev/null @@ -1,158 +0,0 @@ -use std::collections::BTreeMap; -use std::num::NonZeroU8; - -use miden_hir as hir; - -use super::{SolverError, Stack, ValueOrAlias}; -use crate::codegen::Constraint; - -/// The context associated with an instance of [OperandMovementConstraintSolver]. -/// -/// Contained in this context is the current state of the stack, the expected operands, -/// the constraints on those operands, and metadata about copied operands. -#[derive(Debug)] -pub struct SolverContext { - stack: Stack, - expected: Stack, - copies: CopyInfo, -} -impl SolverContext { - pub fn new( - expected: &[hir::Value], - constraints: &[Constraint], - stack: &crate::codegen::OperandStack, - ) -> Result { - use std::collections::btree_map::Entry; - - // Compute the expected output on the stack, as well as alias/copy information - let mut stack = Stack::from(stack); - let mut expected_output = Stack::default(); - let mut copies = CopyInfo::default(); - for (value, constraint) in expected.iter().rev().zip(constraints.iter().rev()) { - let value = ValueOrAlias::from(*value); - match constraint { - // If we observe a value with move semantics, then it is - // always referencing the original value - Constraint::Move => { - expected_output.push(value); - } - // If we observe a value with copy semantics, then it is - // always referencing an alias, because the original would - // need to be preserved - Constraint::Copy => { - expected_output.push(copies.push(value)); - } - } - } - - // Rename multiple occurrences of the same value on the operand stack, if present - let mut renamed = BTreeMap::::default(); - for operand in stack.iter_mut().rev() { - match renamed.entry(operand.value) { - Entry::Vacant(entry) => { - entry.insert(0); - } - Entry::Occupied(mut entry) => { - let next_id = entry.get_mut(); - *next_id += 1; - operand.value.set_alias(NonZeroU8::new(*next_id).unwrap()); - } - } - } - - // Determine if the stack is already in the desired order - // - // If copies are required we can't consider the stack in order even if - // the operands we want are in the desired order, because we must make - // copies of them anyway. - let requires_copies = !copies.is_empty(); - let is_solved = !requires_copies - && expected_output - .iter() - .rev() - .all(|op| &stack[op.pos as usize] == op); - if is_solved { - return Err(SolverError::AlreadySolved); - } - - Ok(Self { - stack, - expected: expected_output, - copies, - }) - } - - /// Returns the number of operands expected by the current instruction - #[inline] - pub fn arity(&self) -> usize { - self.expected.len() - } - - /// Get a reference to the copy analysis results - #[inline(always)] - pub fn copies(&self) -> &CopyInfo { - &self.copies - } - - /// Get a reference to the state of the stack at the current program point - #[inline(always)] - pub fn stack(&self) -> &Stack { - &self.stack - } - - /// Get a [Stack] representing the state of the stack for a valid solution. - /// - /// NOTE: The returned stack only contains the expected operands, not the full stack - #[inline(always)] - pub fn expected(&self) -> &Stack { - &self.expected - } - - /// Return true if the given stack matches what is expected - /// if a solution was correctly found. - pub fn is_solved(&self, pending: &Stack) -> bool { - debug_assert!(pending.len() >= self.expected.len()); - self.expected.iter().all(|o| pending.contains(o)) - } -} - -#[derive(Debug, Default)] -pub struct CopyInfo { - copies: BTreeMap, - num_copies: u8, -} -impl CopyInfo { - /// Returns the number of copies recorded in this structure - #[inline(always)] - pub fn len(&self) -> usize { - self.num_copies as usize - } - - /// Returns true if there are no copied values - #[inline(always)] - pub fn is_empty(&self) -> bool { - self.num_copies == 0 - } - - /// Push a new copy of `value`, returning an alias of that value - pub fn push(&mut self, value: ValueOrAlias) -> ValueOrAlias { - use std::collections::btree_map::Entry; - - self.num_copies += 1; - match self.copies.entry(value) { - Entry::Vacant(entry) => { - entry.insert(0); - value.copy(unsafe { NonZeroU8::new_unchecked(1) }) - } - Entry::Occupied(mut entry) => { - let next_id = entry.get_mut(); - *next_id += 1; - value.copy(NonZeroU8::new(*next_id).unwrap()) - } - } - } - - pub fn has_copies(&self, value: &ValueOrAlias) -> bool { - self.copies.contains_key(value) - } -} diff --git a/codegen/masm/src/codegen/opt/operands/mod.rs b/codegen/masm/src/codegen/opt/operands/mod.rs deleted file mode 100644 index 46081a939..000000000 --- a/codegen/masm/src/codegen/opt/operands/mod.rs +++ /dev/null @@ -1,171 +0,0 @@ -mod context; -mod solver; -mod stack; -mod tactics; - -use self::context::SolverContext; -pub use self::solver::{OperandMovementConstraintSolver, SolverError}; -use self::stack::Stack; - -use std::fmt; -use std::num::NonZeroU8; - -use miden_hir as hir; - -/// This represents a specific action that should be taken by -/// the code generator with regard to an operand on the stack. -/// -/// The output of the optimizer is a sequence of these actions, -/// the effect of which is to place all of the current instruction's -/// operands exactly where they need to be, just when they are -/// needed. -#[derive(Debug, Copy, Clone)] -pub enum Action { - /// Copy the operand at the given index to the top of the stack - Copy(u8), - /// Swap the operand at the given index with the one on top of the stack - Swap(u8), - /// Move the operand at the given index to the top of the stack - MoveUp(u8), - /// Move the operand at the top of the stack to the given index - MoveDown(u8), -} - -/// This is a [miden_hir::Value], but with a modified encoding that lets -/// us uniquely identify aliases of a value on the operand stack during -/// analysis. -/// -/// Aliases of a value are treated as unique values for purposes of operand -/// stack management, but are associated with multiple copies of a value -/// on the stack. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct ValueOrAlias(u32); -impl ValueOrAlias { - const ALIAS_MASK: u32 = (u8::MAX as u32) << 23; - - /// Create a new [Value] with the given numeric identifier. - /// - /// The given identifier must have the upper 8 bits zeroed, or this function will panic. - pub fn new(id: u32) -> Self { - assert_eq!(id & Self::ALIAS_MASK, 0); - Self(id) - } - - /// Create an aliased copy of this value, using `id` to uniquely identify the alias. - /// - /// NOTE: You must ensure that each alias of the same value gets a unique identifier, - /// or you may observe strange behavior due to two aliases that should be distinct, - /// being treated as if they have the same identity. - pub fn copy(self, id: NonZeroU8) -> Self { - Self(self.id() | ((id.get() as u32) << 23)) - } - - /// Get an un-aliased copy of this value - pub fn unaliased(self) -> Self { - Self(self.id()) - } - - /// Convert this value into an alias, using `id` to uniquely identify the alias. - /// - /// NOTE: You must ensure that each alias of the same value gets a unique identifier, - /// or you may observe strange behavior due to two aliases that should be distinct, - /// being treated as if they have the same identity. - pub fn set_alias(&mut self, id: NonZeroU8) { - self.0 = self.id() | ((id.get() as u32) << 23); - } - - /// Get the raw u32 value of the original [miden_hir::Value] - pub fn id(self) -> u32 { - self.0 & !Self::ALIAS_MASK - } - - /// Get the unique alias identifier for this value, if this value is an alias - pub fn alias(self) -> Option { - NonZeroU8::new(((self.0 & Self::ALIAS_MASK) >> 23) as u8) - } - - /// Get the unique alias identifier for this value, if this value is an alias - pub fn unwrap_alias(self) -> NonZeroU8 { - NonZeroU8::new(((self.0 & Self::ALIAS_MASK) >> 23) as u8) - .unwrap_or_else(|| panic!("expected {self:?} to be an alias")) - } - - /// Returns true if this value is an alias - pub fn is_alias(&self) -> bool { - self.0 & Self::ALIAS_MASK != 0 - } -} -impl Ord for ValueOrAlias { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.id().cmp(&other.id()).then_with(|| { - let self_alias = self.alias().map(|nz| nz.get()).unwrap_or(0); - let other_alias = self.alias().map(|nz| nz.get()).unwrap_or(0); - self_alias.cmp(&other_alias) - }) - } -} -impl PartialEq for ValueOrAlias { - fn eq(&self, other: &hir::Value) -> bool { - self.id() == other.as_u32() - } -} -impl PartialOrd for ValueOrAlias { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl From for ValueOrAlias { - #[inline] - fn from(value: hir::Value) -> Self { - Self::new(value.as_u32()) - } -} -impl From for hir::Value { - #[inline] - fn from(value: ValueOrAlias) -> Self { - Self::from_u32(value.id()) - } -} -impl fmt::Debug for ValueOrAlias { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.alias() { - None => write!(f, "v{}", self.id()), - Some(alias) => write!(f, "v{}.{alias}", self.id()), - } - } -} -#[cfg(test)] -impl proptest::arbitrary::Arbitrary for ValueOrAlias { - type Parameters = (); - type Strategy = proptest::strategy::Map, fn(u8) -> Self>; - - fn arbitrary_with((): Self::Parameters) -> Self::Strategy { - use proptest::strategy::Strategy; - proptest::arbitrary::any::().prop_map(|id| ValueOrAlias(id as u32)) - } -} - -/// This is an simple representation of an operand on the operand stack -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Operand { - /// The position of this operand on the corresponding stack - pub pos: u8, - /// The value this operand corresponds to - pub value: ValueOrAlias, -} -impl From<(usize, ValueOrAlias)> for Operand { - #[inline(always)] - fn from(pair: (usize, ValueOrAlias)) -> Self { - Self { - pos: pair.0 as u8, - value: pair.1, - } - } -} -impl PartialEq for Operand { - #[inline(always)] - fn eq(&self, other: &ValueOrAlias) -> bool { - self.value.eq(other) - } -} diff --git a/codegen/masm/src/codegen/opt/operands/solver.rs b/codegen/masm/src/codegen/opt/operands/solver.rs deleted file mode 100644 index dab0fe4a7..000000000 --- a/codegen/masm/src/codegen/opt/operands/solver.rs +++ /dev/null @@ -1,732 +0,0 @@ -use miden_hir as hir; -use smallvec::SmallVec; - -use crate::codegen::Constraint; - -use super::{tactics::Tactic, *}; - -/// This error type is produced by the [OperandMovementConstraintSolver] -#[derive(Debug)] -pub enum SolverError { - /// The current operand stack represents a valid solution already - AlreadySolved, - /// All of the tactics we tried failed - NoSolution, -} - -/// The [OperandMovementConstraintSolver] is used to produce a solution to the following problem: -/// -/// An instruction is being emitted which requires some specific set of operands, in a particular order. -/// These operands are known to be on the operand stack, but their usage is constrained by a rule that -/// determines whether a specific use of an operand can consume the operand, or must copy it and consume -/// the copy. Furthermore, the operands on the stack are not guaranteed to be in the desired order, so we -/// must also move operands into position while operating within the bounds of the move/copy constraints. -/// -/// Complicating matters further, a naive approach to solving this problem will produce a lot of unnecessary -/// stack manipulation instructions in the emitted code. We would like the code we emit to match what a human -/// might write if facing the same set of constraints. As a result, we are looking for a solution to this -/// problem that is also the "smallest" solution, i.e. the least expensive solution in terms of cycle count. -/// -/// ## Implementation -/// -/// With that context in mind, what we have here is a non-trivial optimization problem. If we could treat the -/// operand stack as an array, and didn't have to worry about copies, we could solve this using a standard -/// minimum-swap solution, but neither of those are true here. The copy constraint, when present, means -/// that even if the stack is in the exact order we need, we must still find a way to copy the operands -/// we are required to copy, move the ones we are required to consume, and do so in such a way that getting -/// them into the required order on top of the stack takes the minimum number of steps. -/// -/// Even this would be relatively straightforward, but an additional problem is that the MASM instruction -/// set does not provide us a way to swap two operands at arbitrary positions on the stack. We are forced -/// to move operands to the top of the stack before we can move them elsewhere (either by swapping them -/// with the current operand on top of the stack, or by moving the operand up to the top, shifting all -/// the remaining operands on the stack down by one). However, moving a value up/down the stack also has -/// the effect of shifting other values on the stack, which may shift them in to, or out of, position. -/// -/// Long story short, all of this must be taken into consideration at once, which is extremely difficult -/// to express in a way that is readable/maintainable, but also debuggable if something goes wrong. -/// -/// To address these concerns, the [OperandMovementConstraintSolver] is architected as follows: -/// -/// We expect to receive as input to the solver: -/// -/// * The set of expected operand values -/// * The set of move/copy constraints corresponding to each of the expected operands -/// * The current state of the operand stack at this point in the program -/// -/// The solver produces one of three possible outcomes: -/// -/// * `Ok(solution)`, where `solution` is a vector of actions the code generator must take to get the operands into place correctly -/// * `Err(AlreadySolved)`, indicating that the solver is not needed, and the stack is usable as-is -/// * `Err(_)`, indicating an unrecoverable error that prevented the solver from finding a solution with the given inputs -/// -/// When the solver is constructed, it performs the following steps: -/// -/// 1. Identify and rename aliased values to make them unique (i.e. multiple uses of the same value will be uniqued) -/// 2. Determine if any expected operands require copying (if so, then the solver is always required) -/// 3. Determine if the solver is required for the given inputs, and if not, return `Err(AlreadySolved)` -/// -/// When the solver is run, it attempts to find an optimal solution using the following algorithm: -/// -/// 1. Pick a tactic to try and produce a solution for the given set of constraints. -/// 2. If the tactic failed, go back to step 1. -/// 3. If the tactic succeeded, take the best solution between the one we just produced, and the -/// last one produced (if applicable). -/// 4. If we have optimization fuel remaining, go back to step 1 and see if we can find a better solution. -/// 5. If we have a solution, and either run out of optimization fuel, or tactics to try, then that solution is returned. -/// 6. If we haven't found a solution, then return an error -pub struct OperandMovementConstraintSolver { - context: SolverContext, - tactics: SmallVec<[Box; 4]>, - /// An integer representing the amount of optimization fuel we have available - fuel: usize, -} -impl OperandMovementConstraintSolver { - /// Construct a new solver for the given expected operands, constraints, and operand stack state. - pub fn new( - expected: &[hir::Value], - constraints: &[Constraint], - stack: &crate::codegen::OperandStack, - ) -> Result { - assert_eq!(expected.len(), constraints.len()); - - let context = SolverContext::new(expected, constraints, stack)?; - - Ok(Self { - context, - tactics: Default::default(), - fuel: 25, - }) - } - - /// Set the quantity of optimization fuel the solver has to work with - #[allow(unused)] - pub fn set_optimization_fuel(&mut self, fuel: usize) { - self.fuel = fuel; - } - - /// Compute a solution that can be used to get the stack into the correct state - pub fn solve(mut self) -> Result, SolverError> { - use super::tactics::*; - - // We use a few heuristics to guide which tactics we try: - // - // * If all operands are copies, we only apply copy-all - // * If copies are needed, we only apply tactics which - // support copies, or a mix of copies and moves. - // * If no copies are needed, we start with the various - // move up/down + swap patterns, as many common patterns - // are solved in two moves or less with them. If no tactics - // are successful, move-all is used as the fallback. - // * If we have no optimization fuel, we do not attempt to - // look for better solutions once we've found one. - // * If we have optimization fuel, we will try additional - // tactics looking for a solution until we have exhausted - // the fuel, assuming the solution we do have can be - // minimized. For example, a solution which requires less - // than two actions is by definition optimal already, so - // we never waste time on optimization in such cases. - - // The tactics are pushed in reverse order - if self.tactics.is_empty() { - if self.context.copies().is_empty() { - self.tactics.push(Box::new(Linear)); - self.tactics.push(Box::new(SwapAndMoveUp)); - self.tactics.push(Box::new(MoveUpAndSwap)); - self.tactics.push(Box::new(MoveDownAndSwap)); - } else { - self.tactics.push(Box::new(Linear)); - self.tactics.push(Box::new(CopyAll)); - } - } - - // Now that we know what constraints are in place, we can derive - // a strategy to solve for those constraints. The overall strategy - // is a restricted backtracking search based on a number of predefined - // tactics for permuting the stack. The search is restricted because - // we do not try every possible combination of tactics, and instead - // follow a shrinking strategy that always subdivides the problem if - // a larger tactic doesn't succeed first. The search proceeds until - // a solution is derived, or we cannot proceed any further, in which - // case we fall back to the most naive approach possible - copying - // items to the top of the stack one after another until all arguments - // are in place. - // - // Some tactics are derived simply by the number of elements involved, - // others based on the fact that all copies are required, or all moves. - // Many solutions are trivially derived from a given set of constraints, - // we aim simply to recognize common patterns recognized by a human and - // apply those solutions in such a way that we produce code like we would - // by hand when preparing instruction operands - let mut best_solution: Option> = None; - let mut builder = SolutionBuilder::new(&self.context); - while let Some(mut tactic) = self.tactics.pop() { - match tactic.apply(&mut builder) { - // The tactic was applied successfully - Ok(_) => { - if builder.is_valid() { - let solution = builder.take(); - let solution_size = solution.len(); - let best_size = best_solution.as_ref().map(|best| best.len()); - match best_size { - Some(best_size) if best_size > solution_size => { - best_solution = Some(solution); - log::debug!("a better solution ({solution_size} vs {best_size}) was found using tactic {}", tactic.name()); - } - Some(best_size) => { - log::debug!("a solution of size {solution_size} was found using tactic {}, but it is no better than the best found so far ({best_size})", tactic.name()); - } - None => { - best_solution = Some(solution); - log::debug!("an initial solution of size {solution_size} was found using tactic {}", tactic.name()); - } - } - } else { - log::debug!("a partial solution was found using tactic {}, but is not sufficient on its own", tactic.name()); - builder.discard(); - } - } - Err(_) => { - log::debug!("tactic {} could not be applied", tactic.name()); - builder.discard(); - } - } - let remaining_fuel = self.fuel.saturating_sub(tactic.cost(&self.context)); - if remaining_fuel == 0 { - log::debug!("no more optimization fuel, using the best solution found so far"); - break; - } - self.fuel = remaining_fuel; - } - - best_solution.take().ok_or(SolverError::NoSolution) - } - - #[track_caller] - pub fn solve_and_apply( - self, - emitter: &mut crate::codegen::emit::OpEmitter<'_>, - ) -> Result<(), SolverError> { - match self.context.arity() { - // No arguments, nothing to solve - 0 => Ok(()), - // Only one argument, solution is trivial - 1 => { - 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); - } - } else { - assert!( - self.context.copies().has_copies(&expected.value), - "{:?} was not found on the operand stack", - expected.value - ); - let current_position = self - .context - .stack() - .position(&expected.value.unaliased()) - .unwrap_or_else(|| { - panic!( - "{:?} was not found on the operand stack", - expected.value.unaliased() - ) - }); - emitter.copy_operand_to_position(current_position, 0, false); - } - - Ok(()) - } - // Run the solver for more than 1 argument - _ => { - let actions = self.solve()?; - for action in actions.into_iter() { - match action { - Action::Copy(index) => { - emitter.copy_operand_to_position(index as usize, 0, false); - } - Action::Swap(index) => { - emitter.swap(index); - } - Action::MoveUp(index) => { - emitter.movup(index); - } - Action::MoveDown(index) => { - emitter.movdn(index); - } - } - } - - Ok(()) - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use miden_hir::{self as hir, Type}; - use proptest::prelude::*; - use proptest::strategy::Just; - use proptest::test_runner::TestRunner; - - #[allow(unused)] - fn setup() { - use log::LevelFilter; - let _ = env_logger::builder() - .filter_level(LevelFilter::Trace) - .format_timestamp(None) - .is_test(true) - .try_init(); - } - - #[test] - fn operand_movement_constraint_solver_example() { - let v1 = hir::Value::from_u32(1); - let v2 = hir::Value::from_u32(2); - let v3 = hir::Value::from_u32(3); - let v4 = hir::Value::from_u32(4); - let v5 = hir::Value::from_u32(5); - let v6 = hir::Value::from_u32(6); - - let tests = [[v2, v1, v3, v4, v5, v6], [v2, v4, v3, v1, v5, v6]]; - - for test in tests.into_iter() { - let mut stack = crate::codegen::OperandStack::default(); - for value in test.into_iter().rev() { - stack.push(crate::codegen::TypedValue { - ty: Type::I32, - value, - }); - } - let expected = [v1, v2, v3, v4, v5]; - let constraints = [Constraint::Move; 5]; - - match OperandMovementConstraintSolver::new(&expected, &constraints, &stack) { - Ok(solver) => { - let result = solver.solve().expect("no solution found"); - assert!(result.len() <= 3, "expected solution of 3 moves or less"); - } - Err(SolverError::AlreadySolved) => panic!("already solved"), - Err(err) => panic!("invalid solver context: {err:?}"), - } - } - } - - #[test] - fn operand_movement_constraint_solver_two_moves() { - let v1 = hir::Value::from_u32(1); - let v2 = hir::Value::from_u32(2); - let v3 = hir::Value::from_u32(3); - let v4 = hir::Value::from_u32(4); - let v5 = hir::Value::from_u32(5); - let v6 = hir::Value::from_u32(6); - - // Should take two moves - let tests = [ - [v5, v4, v2, v3, v1, v6], - [v4, v5, v1, v2, v3, v6], - [v5, v2, v1, v3, v4, v6], - [v1, v3, v2, v4, v5, v6], - [v5, v2, v1, v4, v3, v6], - [v1, v3, v4, v2, v5, v6], - [v4, v3, v2, v1, v5, v6], - [v4, v3, v2, v1, v5, v6], - ]; - - for test in tests.into_iter() { - let mut stack = crate::codegen::OperandStack::default(); - for value in test.into_iter().rev() { - stack.push(crate::codegen::TypedValue { - ty: Type::I32, - value, - }); - } - let expected = [v1, v2, v3, v4, v5]; - let constraints = [Constraint::Move; 5]; - - match OperandMovementConstraintSolver::new(&expected, &constraints, &stack) { - Ok(solver) => { - let result = solver.solve().expect("no solution found"); - assert!( - result.len() <= 2, - "expected solution of 2 moves or less, got {result:?}" - ); - } - Err(SolverError::AlreadySolved) => panic!("already solved"), - Err(err) => panic!("invalid solver context: {err:?}"), - } - } - } - - #[test] - fn operand_movement_constraint_solver_one_move() { - let v1 = hir::Value::from_u32(1); - let v2 = hir::Value::from_u32(2); - let v3 = hir::Value::from_u32(3); - let v4 = hir::Value::from_u32(4); - let v5 = hir::Value::from_u32(5); - let v6 = hir::Value::from_u32(6); - - // Should take one move - let tests = [ - [v2, v3, v1, v4, v5, v6], - [v4, v1, v2, v3, v5, v6], - [v4, v2, v3, v1, v5, v6], - [v2, v1, v3, v4, v5, v6], - ]; - - for test in tests.into_iter() { - let mut stack = crate::codegen::OperandStack::default(); - for value in test.into_iter().rev() { - stack.push(crate::codegen::TypedValue { - ty: Type::I32, - value, - }); - } - let expected = [v1, v2, v3, v4, v5]; - let constraints = [Constraint::Move; 5]; - - match OperandMovementConstraintSolver::new(&expected, &constraints, &stack) { - Ok(solver) => { - let result = solver.solve().expect("no solution found"); - assert!( - result.len() <= 1, - "expected solution of 1 move or less, got {result:?}" - ); - } - Err(SolverError::AlreadySolved) => panic!("already solved"), - Err(err) => panic!("invalid solver context: {err:?}"), - } - } - } - - // Strategy: - // - // 1. Generate a set of 1..16 operands to form a stack (called `stack`), with no more than 2 pairs of duplicate operands - // 2. Generate a set of up to 8 constraints (called `constraints`) by sampling `stack` twice, and treating duplicate samples as copies - // 3. Generate the set of expected operands by mapping `constraints` to values - #[derive(Debug)] - struct ProblemInputs { - stack: crate::codegen::OperandStack, - expected: Vec, - constraints: Vec, - } - - fn shuffled_value_stack(size: usize) -> proptest::strategy::Shuffle>> { - let mut next_id = 0; - let mut raw_stack = Vec::with_capacity(size); - raw_stack.resize_with(size, || { - let id = next_id; - next_id += 1; - hir::Value::from_u32(id) - }); - Just(raw_stack).prop_shuffle() - } - - fn copy_all(arity: usize) -> impl Strategy { - Just((1usize << arity) - 1) - } - - fn copy_some(range: core::ops::RangeInclusive) -> impl Strategy { - let max = *range.end(); - proptest::bits::usize::sampled(0..max, range) - } - - fn copy_any(arity: usize) -> impl Strategy { - let min = core::cmp::min(1, arity); - prop_oneof![ - CopyStrategy::all(arity), - CopyStrategy::none(arity), - CopyStrategy::some(min..=arity), - ] - } - - #[derive(Debug, Clone)] - struct CopyStrategy { - strategy: proptest::strategy::BoxedStrategy, - arity: u8, - min: u8, - max: u8, - } - impl CopyStrategy { - /// The simplest strategy, always solvable by copying - pub fn all(arity: usize) -> Self { - assert!(arity <= 16); - let max = arity as u8; - let strategy = if arity == 0 { - Just(0usize).boxed() - } else if arity == 1 { - Just(1usize).boxed() - } else { - proptest::bits::usize::sampled(1..arity, 0..arity).boxed() - }; - Self { - strategy, - arity: max, - min: max, - max, - } - } - - /// The next simplest strategy, avoids complicating strategies with copies - pub fn none(arity: usize) -> Self { - assert!(arity <= 16); - let max = arity as u8; - let strategy = if arity == 0 { - Just(0usize).boxed() - } else if arity == 1 { - Just(1usize).boxed() - } else { - proptest::bits::usize::sampled(1..arity, 0..arity).boxed() - }; - Self { - strategy, - arity: max, - min: 0, - max: 0, - } - } - - /// The most complicated strategy, - pub fn some(range: core::ops::RangeInclusive) -> Self { - let min = *range.start(); - let max = *range.end(); - assert!(max <= 16); - let strategy = if max == 0 { - Just(0usize).boxed() - } else if max == 1 { - Just(1usize).boxed() - } else { - proptest::bits::usize::sampled(0..max, range).boxed() - }; - let arity = max as u8; - Self { - strategy, - arity, - min: min as u8, - max: arity, - } - } - } - impl Strategy for CopyStrategy { - type Tree = CopyStrategyValueTree; - type Value = usize; - fn new_tree(&self, runner: &mut TestRunner) -> proptest::strategy::NewTree { - let tree = self.strategy.new_tree(runner)?; - Ok(CopyStrategyValueTree { - tree, - arity: self.arity, - min: self.min, - max: self.max, - prev: (self.min, self.max), - hi: (0, self.max), - }) - } - } - - struct CopyStrategyValueTree { - tree: Box>, - arity: u8, - min: u8, - max: u8, - prev: (u8, u8), - hi: (u8, u8), - } - impl proptest::strategy::ValueTree for CopyStrategyValueTree { - type Value = usize; - - fn current(&self) -> Self::Value { - match (self.min, self.max) { - (0, 0) => 0, - (min, max) if min == max => (1 << max as usize) - 1, - _ => self.tree.current(), - } - } - - fn simplify(&mut self) -> bool { - match (self.min, self.max) { - (0, 0) => { - self.hi = (0, 0); - self.min = self.arity; - self.max = self.arity; - true - } - (min, max) if min == max => { - self.hi = (min, max); - false - } - current => { - self.hi = current; - if !self.tree.simplify() { - self.min = 0; - self.max = 0; - } - true - } - } - } - - fn complicate(&mut self) -> bool { - match (self.min, self.max) { - current if current == self.hi => false, - (0, 0) => { - self.min = self.prev.0; - self.max = self.prev.1; - true - } - (min, max) if min == max => { - self.min = 0; - self.max = 0; - true - } - _ => self.tree.complicate(), - } - } - } - - fn make_problem_inputs( - raw_stack: Vec, - arity: usize, - copies: usize, - ) -> ProblemInputs { - use proptest::bits::BitSetLike; - - let mut stack = crate::codegen::OperandStack::default(); - let mut expected = Vec::with_capacity(arity); - let mut constraints = Vec::with_capacity(arity); - for value in raw_stack.into_iter().rev() { - stack.push(crate::codegen::TypedValue { - ty: hir::Type::I32, - value, - }); - } - for id in 0..arity { - let value = hir::Value::from_u32(id as u32); - expected.push(value); - if copies.test(id) { - constraints.push(Constraint::Copy); - } else { - constraints.push(Constraint::Move); - } - } - ProblemInputs { - stack, - expected, - constraints, - } - } - - prop_compose! { - fn generate_copy_any_problem()((raw_stack, arity) in (1..8usize).prop_flat_map(|stack_size| (shuffled_value_stack(stack_size), 0..=stack_size))) - (copies in copy_any(arity), raw_stack in Just(raw_stack), arity in Just(arity)) -> ProblemInputs { - make_problem_inputs(raw_stack, arity, copies) - } - } - - prop_compose! { - fn generate_copy_none_problem()((raw_stack, arity) in (1..8usize).prop_flat_map(|stack_size| (shuffled_value_stack(stack_size), 0..=stack_size))) - (raw_stack in Just(raw_stack), arity in Just(arity)) -> ProblemInputs { - make_problem_inputs(raw_stack, arity, 0) - } - } - - prop_compose! { - fn generate_copy_all_problem()((raw_stack, arity) in (1..8usize).prop_flat_map(|stack_size| (shuffled_value_stack(stack_size), 0..=stack_size))) - (copies in copy_all(arity), raw_stack in Just(raw_stack), arity in Just(arity)) -> ProblemInputs { - make_problem_inputs(raw_stack, arity, copies) - } - } - - prop_compose! { - fn generate_copy_some_problem()((raw_stack, arity) in (1..8usize).prop_flat_map(|stack_size| (shuffled_value_stack(stack_size), 1..=stack_size))) - (copies in copy_some(1..=arity), raw_stack in Just(raw_stack), arity in Just(arity)) -> ProblemInputs { - make_problem_inputs(raw_stack, arity, copies) - } - } - - fn run_solver(problem: ProblemInputs) -> Result<(), TestCaseError> { - match OperandMovementConstraintSolver::new( - &problem.expected, - &problem.constraints, - &problem.stack, - ) { - Ok(mut solver) => { - solver.set_optimization_fuel(10); - let result = solver.solve(); - // We are expecting solutions for all inputs - prop_assert!( - result.is_ok(), - "solver returned error {result:?} for problem: {problem:#?}" - ); - let actions = result.unwrap(); - // We are expecting that if all operands are copies, that the number of actions is equal to the number of copies - if problem - .constraints - .iter() - .all(|c| matches!(c, Constraint::Copy)) - { - prop_assert_eq!(actions.len(), problem.expected.len()); - } - // We are expecting that applying `actions` to the input stack will produce a stack that - // has all of the expected operands on top of the stack, ordered by id, e.g. [v1, v2, ..vN] - let mut stack = problem.stack.clone(); - for action in actions.into_iter() { - match action { - Action::Copy(index) => { - stack.dup(index as usize); - } - Action::Swap(index) => { - stack.swap(index as usize); - } - Action::MoveUp(index) => { - stack.movup(index as usize); - } - Action::MoveDown(index) => { - stack.movdn(index as usize); - } - } - } - for index in 0..problem.expected.len() { - let expected = hir::Value::from_u32(index as u32); - prop_assert_eq!( - &stack[index], - &expected, - "solution did not place {} at the correct location on the stack", - expected - ); - } - - Ok(()) - } - Err(SolverError::AlreadySolved) => Ok(()), - Err(err) => panic!("invalid solver context: {err:?}"), - } - } - - proptest! { - #![proptest_config(ProptestConfig::with_cases(1000))] - - #[test] - fn operand_movement_constraint_solver_copy_any(problem in generate_copy_any_problem()) { - run_solver(problem)? - } - - #[test] - fn operand_movement_constraint_solver_copy_none(problem in generate_copy_none_problem()) { - run_solver(problem)?; - } - - #[test] - fn operand_movement_constraint_solver_copy_all(problem in generate_copy_all_problem()) { - run_solver(problem)?; - } - - #[test] - fn operand_movement_constraint_solver_copy_some(problem in generate_copy_some_problem()) { - run_solver(problem)?; - } - } -} diff --git a/codegen/masm/src/codegen/opt/operands/stack.rs b/codegen/masm/src/codegen/opt/operands/stack.rs deleted file mode 100644 index 7b1c1148d..000000000 --- a/codegen/masm/src/codegen/opt/operands/stack.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::collections::VecDeque; - -use super::*; - -/// This implements a stack data structure for [Operand] -#[derive(Default, Debug, Clone)] -pub struct Stack { - stack: Vec, -} -impl FromIterator for Stack { - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - let mut stack = VecDeque::new(); - for value in iter.into_iter() { - stack.push_front(Operand { pos: 0, value }); - } - let mut stack = Vec::from(stack); - for (pos, operand) in stack.iter_mut().rev().enumerate() { - operand.pos = pos as u8; - } - Self { stack } - } -} -impl From<&crate::codegen::OperandStack> for Stack { - fn from(stack: &crate::codegen::OperandStack) -> Self { - Self::from_iter(stack.iter().rev().map(|o| { - o.as_value() - .unwrap_or_else(|| panic!("expected value operand, got {o:#?}")) - .into() - })) - } -} -impl Stack { - pub fn len(&self) -> usize { - self.stack.len() - } - - pub fn push(&mut self, value: ValueOrAlias) { - self.stack.push(Operand { pos: 0, value }); - if self.stack.len() > 1 { - for (pos, operand) in self.iter_mut().rev().enumerate() { - operand.pos = pos as u8; - } - } - } - - pub fn position(&self, value: &ValueOrAlias) -> Option { - self.stack.iter().rev().position(|o| value == &o.value) - } - - pub fn contains(&self, operand: &Operand) -> bool { - self[operand.pos as usize].value == operand.value - } - - pub fn iter(&self) -> impl DoubleEndedIterator { - self.stack.iter() - } - - pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { - self.stack.iter_mut() - } - - pub fn dup(&mut self, n: usize, alias_id: core::num::NonZeroU8) { - let value = self[n].value; - self.stack.push(Operand { - pos: 0, - value: value.copy(alias_id), - }); - for (pos, operand) in self.stack.iter_mut().rev().enumerate() { - operand.pos = pos as u8; - } - } - - pub fn swap(&mut self, n: usize) { - let len = self.stack.len(); - let a = len - 1; - let b = a - n; - let a_pos = self.stack[a].pos; - let b_pos = self.stack[b].pos; - self.stack.swap(a, b); - self.stack[a].pos = a_pos; - self.stack[b].pos = b_pos; - } - - pub fn movup(&mut self, n: usize) { - let len = self.stack.len(); - let mid = len - (n + 1); - let (_, r) = self.stack.split_at_mut(mid); - r.rotate_left(1); - for (pos, operand) in r.iter_mut().rev().enumerate() { - operand.pos = pos as u8; - } - } - - pub fn movdn(&mut self, n: usize) { - let len = self.stack.len(); - let mid = len - (n + 1); - let (_, r) = self.stack.split_at_mut(mid); - r.rotate_right(1); - for (pos, operand) in r.iter_mut().rev().enumerate() { - operand.pos = pos as u8; - } - } - - pub fn reset_to(&mut self, snapshot: &Self) { - self.stack.clear(); - let x = self.stack.capacity(); - let y = snapshot.stack.capacity(); - if x != y { - let a = core::cmp::max(x, y); - if a > x { - self.stack.reserve(a - x); - } - } - self.stack.extend_from_slice(&snapshot.stack); - } - - pub fn get(&self, index: usize) -> Option<&Operand> { - let len = self.stack.len(); - self.stack.get(len - index - 1) - } -} -impl core::ops::Index for Stack { - type Output = Operand; - - fn index(&self, index: usize) -> &Self::Output { - let len = self.stack.len(); - &self.stack[len - index - 1] - } -} -impl core::ops::IndexMut for Stack { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - let len = self.stack.len(); - &mut self.stack[len - index - 1] - } -} diff --git a/codegen/masm/src/codegen/opt/operands/tactics/copy_all.rs b/codegen/masm/src/codegen/opt/operands/tactics/copy_all.rs deleted file mode 100644 index a4f66bc06..000000000 --- a/codegen/masm/src/codegen/opt/operands/tactics/copy_all.rs +++ /dev/null @@ -1,58 +0,0 @@ -use super::*; - -/// This tactic simply copies all expected operands right-to-left. -/// -/// As a precondition, this tactic requires that all expected operands are copies. -#[derive(Default)] -pub struct CopyAll; -impl Tactic for CopyAll { - fn cost(&self, context: &SolverContext) -> usize { - core::cmp::max(context.copies().len(), 1) - } - - fn apply(&mut self, builder: &mut SolutionBuilder) -> TacticResult { - // We can't apply this tactic if any values should be moved - let arity = builder.arity(); - if builder.num_copies() != arity { - log::debug!("expected all operands to require copying; but only {} out of {} operands are copied", builder.num_copies(), arity); - return Err(TacticError::PreconditionFailed); - } - - // Visit the expected operands in bottom-up order copying them as we go - for index in (0..(arity as u8)).rev() { - let expected_value = builder.unwrap_expected(index); - // Because we create aliases for all copies we expect, as well as copies already - // present on the stack, we won't find the expected value (which is an alias) unless - // a copy already exists. As things are today, we should never even hit this branch, - // since we should be copying-on-demand, and thus never leaving copies on the stack - // across instructions, however we gracefully handle the case here, should we ever - // add passes which proactively introduce copies on the stack during lowering. - // - // In short, if we find a copy on the stack, we don't make another copy, we use - // the existing one. Otherwise, we copy as usual. - if let Some(current_position) = builder.get_current_position(&expected_value) { - if current_position == index { - log::trace!("{expected_value:?} is at its expected index {current_position}"); - continue; - } - - log::trace!("moving {expected_value:?} at index {index} up to top of stack, shifting {:?} down one", builder.unwrap_current(0)); - builder.movup(current_position); - } else { - let current_position = builder - .get_current_position(&expected_value.unaliased()) - .unwrap_or_else(|| { - panic!( - "expected {:?} on the stack, but it was not found", - expected_value.unaliased() - ) - }); - // A copy already exists, so use it - log::trace!("copying {expected_value:?} at index {index} to top of stack, shifting {:?} down one", builder.unwrap_current(0)); - builder.dup(current_position, expected_value.unwrap_alias()); - } - } - - Ok(()) - } -} diff --git a/codegen/masm/src/codegen/opt/operands/tactics/linear.rs b/codegen/masm/src/codegen/opt/operands/tactics/linear.rs deleted file mode 100644 index b67e15826..000000000 --- a/codegen/masm/src/codegen/opt/operands/tactics/linear.rs +++ /dev/null @@ -1,213 +0,0 @@ -use petgraph::prelude::{DiGraphMap, Direction}; - -use miden_hir::adt::SmallSet; - -use super::*; - -/// This tactic produces a solution for the given constraints by traversing -/// the stack top-to-bottom, copying/evicting/swapping as needed to put -/// the expected value for the current working index in place. -/// -/// This tactic does make an effort to avoid needless moves by searching -/// for swap opportunities that will place multiple expected operands in -/// place at once using the optimal number of swaps. In cases where this -/// cannot be done however, it will perform as few swaps as it can while -/// still making progress. -#[derive(Default)] -pub struct Linear; -impl Tactic for Linear { - fn cost(&self, context: &SolverContext) -> usize { - core::cmp::max(context.copies().len(), 1) - } - - fn apply(&mut self, builder: &mut SolutionBuilder) -> TacticResult { - let mut graph = DiGraphMap::::new(); - - // Materialize copies - let mut materialized = SmallSet::::default(); - for b in builder.context().expected().iter().rev() { - // Where is B - if let Some(_b_at) = builder.get_current_position(&b.value) { - log::trace!( - "no copy needed for {:?} from index {} to top of stack", - b.value, - b.pos - ); - materialized.insert(b.value); - } else { - // B isn't on the stack because it is a copy we haven't materialized yet - assert!(b.value.is_alias()); - let b_at = builder.unwrap_current_position(&b.value.unaliased()); - log::trace!( - "materializing copy of {:?} from index {} to top of stack", - b.value, - b.pos - ); - builder.dup(b_at, b.value.unwrap_alias()); - materialized.insert(b.value); - } - } - - // Visit each materialized operand and, if out of place, add it to the graph - // along with the node occupying its expected location on the stack. The occupying - // node is then considered materialized and visited as well. - let mut current_index = 0; - let mut materialized = materialized.into_vec(); - loop { - if current_index >= materialized.len() { - break; - } - let value = materialized[current_index]; - let currently_at = builder.unwrap_current_position(&value); - if let Some(expected_at) = builder.get_expected_position(&value) { - if currently_at == expected_at { - log::trace!( - "{value:?} at index {currently_at} is expected there, no movement needed" - ); - current_index += 1; - continue; - } - let occupied_by = builder.unwrap_current(expected_at); - log::trace!("{value:?} at index {currently_at}, is expected at index {expected_at}, which is currently occupied by {occupied_by:?}"); - let from = graph.add_node(Operand { - pos: currently_at, - value, - }); - let to = graph.add_node(Operand { - pos: expected_at, - value: occupied_by, - }); - graph.add_edge(from, to, ()); - if !materialized.contains(&occupied_by) { - materialized.push(occupied_by); - } - } else { - // `value` is not an expected operand, but is occupying a spot - // on the stack needed by one of the expected operands. We can - // create a connected component with `value` by finding the root - // of the path which leads to `value` from an expected operand, - // then adding an edge from `value` back to that operand. This - // forms a cycle which will allow all expected operands to be - // swapped into place, and the unused operand evicted, without - // requiring excess moves. - let operand = Operand { - pos: currently_at, - value, - }; - let mut parent = graph - .neighbors_directed(operand, Direction::Incoming) - .next(); - // There must have been an immediate parent to `value`, or it would - // have an expected position on the stack, and only expected operands - // are materialized initially. - let mut root = parent.unwrap(); - log::trace!("{value:?} at index {currently_at}, is not an expected operand; but must be moved to make space for {:?}", root.value); - let mut seen = std::collections::BTreeSet::default(); - seen.insert(root); - while let Some(parent_operand) = parent { - root = parent_operand; - parent = graph - .neighbors_directed(parent_operand, Direction::Incoming) - .next(); - } - log::trace!("forming component with {value:?} by adding edge to {:?}, the start of the path which led to it", root.value); - graph.add_edge(operand, root, ()); - } - current_index += 1; - } - - // Compute the strongly connected components of the graph we've constructed, - // and use that to drive our decisions about moving operands into place. - let components = petgraph::algo::kosaraju_scc(&graph); - log::trace!("found the following connected components when analyzing required operand moves: {components:?}"); - for component in components.into_iter() { - // A component of two or more elements indicates a cycle of operands. - // - // To determine the order in which swaps must be performed, we first look - // to see if any of the elements are on top of the stack. If so, we swap - // it with its parent in the graph, and so on until we reach the edge that - // completes the cycle (i.e. brings us back to the operand we started with). - // - // If we didn't have an operand on top of the stack yet, we pick the operand - // that is closest to the top of the stack to move to the top, so as not to - // disturb the positions of the other operands. We then proceed as described - // above. The only additional step required comes at the end, where we move - // whatever operand ended up on top of the stack to the original position of - // the operand we started with. - // - // # Examples - // - // Consider a component of 3 operands: B -> A -> C -> B - // - // We can put all three operands in position by first swapping B with A, - // putting B into position; and then A with C, putting A into position, - // and leaving C in position as a result. - // - // Let's extend it one operand further: B -> A -> C -> D -> B - // - // The premise is the same, B with A, A with C, then C with D, the result - // is that they all end up in position at the end. - // - // Here's a diagram of how the state changes as we perform the swaps - // - // 0 1 2 3 - // C -> D -> B -> A -> C - // - // 0 1 2 3 - // D C B A - // - // 0 1 2 3 - // B C D A - // - // 0 1 2 3 - // A C D B - // - if component.len() > 1 { - // Find the operand at the shallowest depth on the stack to move. - let start = component - .iter() - .min_by(|a, b| a.pos.cmp(&b.pos)) - .copied() - .unwrap(); - log::trace!( - "resolving component {component:?} by starting from {:?} at index {}", - start.value, - start.pos - ); - - // If necessary, move the starting operand to the top of the stack - let start_position = start.pos; - if start_position > 0 { - builder.movup(start_position); - } - - // Do the initial swap to set up our state for the remaining swaps - let mut child = graph - .neighbors_directed(start, Direction::Outgoing) - .next() - .unwrap(); - // Swap each child with its parent until we reach the edge that forms a cycle - while child != start { - log::trace!( - "swapping {:?} with {:?} at index {}", - builder.unwrap_current(0), - child.value, - child.pos - ); - builder.swap(child.pos); - child = graph - .neighbors_directed(child, Direction::Outgoing) - .next() - .unwrap(); - } - - // If necessary, move the final operand to the original starting position - if start_position > 0 { - builder.movdn(start_position) - } - } - } - - Ok(()) - } -} diff --git a/codegen/masm/src/codegen/opt/operands/tactics/mod.rs b/codegen/masm/src/codegen/opt/operands/tactics/mod.rs deleted file mode 100644 index 04866b09b..000000000 --- a/codegen/masm/src/codegen/opt/operands/tactics/mod.rs +++ /dev/null @@ -1,275 +0,0 @@ -use core::num::NonZeroU8; - -use super::{Action, Operand, SolverContext, Stack, ValueOrAlias}; - -mod copy_all; -mod linear; -mod move_down_and_swap; -mod move_up_and_swap; -mod swap_and_move_up; - -pub use self::copy_all::CopyAll; -pub use self::linear::Linear; -pub use self::move_down_and_swap::MoveDownAndSwap; -pub use self::move_up_and_swap::MoveUpAndSwap; -pub use self::swap_and_move_up::SwapAndMoveUp; - -/// An error returned by an [OperandMovementConstraintSolver] tactic -#[derive(Debug)] -pub enum TacticError { - /// The tactic could not be applied due to a precondition - /// that is required for the tactic to succeed. For example, - /// a tactic that does not handle copies will have a precondition - /// that there are no copy constraints, and will not attempt - /// to compute a solution if there are. - PreconditionFailed, - /// The tactic could not be applied because the pattern it - /// looks for could not be found in the current context. - NotApplicable, -} - -/// The type of result produced by a [Tactic] -pub type TacticResult = Result<(), TacticError>; - -/// A [Tactic] implements an algorithm for solving operand movement constraints -/// that adhere to a specific pattern or patterns. -/// -/// Tactics should attempt to fail early by first recognizing whether the state -/// of the stack adheres to the pattern which the tactic is designed to solve, -/// and only then should it actually compute the specific actions needed to lay -/// out the stack as expected. -/// -/// A tactic does not need to check if the result of computing a solution actually -/// solves all of the constraints, that is done by [OperandMovementConstraintSolver]. -/// -/// Tactics can have an associated cost, which is used when iterating over multiple -/// tactics looking for the best solution. You should strive to make the cost reflect -/// the computational complexity of the tactic to the degree possible. The default -/// cost for all tactics is 1. -pub trait Tactic { - /// The name of this tactic to use in informational messages. - /// - /// The default name of each tactic is the name of the implementing type. - fn name(&self) -> &'static str { - let name = core::any::type_name::(); - match name.find(|c: char| c.is_ascii_uppercase()) { - None => name, - Some(index) => name.split_at(index).1, - } - } - - /// The computational cost of this tactic in units of optimization fuel. - /// - /// The provided context can be used to compute a cost dynamically based on - /// the number of expected operands, the constraints, and the size of the stack. - /// - /// The default cost is 1. - fn cost(&self, _context: &SolverContext) -> usize { - 1 - } - - /// Apply this tactic using the provided [SolutionBuilder]. - fn apply(&mut self, builder: &mut SolutionBuilder) -> TacticResult; -} - -/// This struct is constructed by an [OperandMovementConstraintSolver], and provided -/// to each [Tactic] it applies in search of a solution. -/// -/// The purpose of this builder is to abstract over the solver context, the pending -/// state of the stack, and to ensure that the solution computed by a [Tactic] is -/// captured accurately. -#[derive(Debug, Clone)] -pub struct SolutionBuilder<'a> { - /// The current solver context - context: &'a SolverContext, - /// The state of the stack after applying `actions` - pending: Stack, - /// The actions that represent the solution constructed so far. - actions: Vec, -} -impl<'a> SolutionBuilder<'a> { - #[doc(hidden)] - pub fn new(context: &'a SolverContext) -> Self { - Self { - context, - pending: context.stack().clone(), - actions: vec![], - } - } - - /// Return the number of operands expected by the current instruction being emitted - pub fn arity(&self) -> usize { - self.context.arity() - } - - /// Return true if the current context requires operand copies to be made - pub fn requires_copies(&self) -> bool { - !self.context.copies().is_empty() - } - - /// Return the total number of copied operands expected - pub fn num_copies(&self) -> usize { - self.context.copies().len() - } - - /// Get a reference to the underlying context of the solver - #[inline(always)] - pub fn context(&self) -> &'a SolverContext { - self.context - } - - /// Get a reference to the state of the stack after applying the pending solution - #[inline(always)] - pub fn stack(&self) -> &Stack { - &self.pending - } - - /// Take the current solution and reset the builder - pub fn take(&mut self) -> Vec { - let actions = core::mem::take(&mut self.actions); - self.pending.reset_to(self.context.stack()); - actions - } - - /// Discard the current solution, and reset back to the initial state - pub fn discard(&mut self) { - self.actions.clear(); - self.pending.reset_to(self.context.stack()); - } - - /// Check if the pending solution is a valid solution - pub fn is_valid(&self) -> bool { - self.context.is_solved(&self.pending) - } - - /// Get the value expected at `index` - pub fn get_expected(&self, index: u8) -> Option { - self.context.expected().get(index as usize).map(|o| o.value) - } - - /// Get the value expected at `index` or panic - #[track_caller] - pub fn unwrap_expected(&self, index: u8) -> ValueOrAlias { - match self.get_expected(index) { - Some(value) => value, - None => panic!( - "expected operand {index} does not exist: there are only {} expected operands", - self.context.arity() - ), - } - } - - /// Get the value currently at `index` in this solution - #[allow(unused)] - pub fn get_current(&self, index: u8) -> Option { - self.pending.get(index as usize).map(|o| o.value) - } - - /// Get the value currently at `index` in this solution - #[track_caller] - pub fn unwrap_current(&self, index: u8) -> ValueOrAlias { - match self.pending.get(index as usize) { - Some(operand) => operand.value, - None => panic!( - "operand {index} does not exist: the stack contains only {} operands", - self.pending.len() - ), - } - } - - /// Get the position at which `value` is expected - pub fn get_expected_position(&self, value: &ValueOrAlias) -> Option { - self.context - .expected() - .position(value) - .map(|index| index as u8) - } - - #[track_caller] - pub fn unwrap_expected_position(&self, value: &ValueOrAlias) -> u8 { - match self.get_expected_position(value) { - Some(pos) => pos, - None => panic!("value {value:?} is not an expected operand"), - } - } - - /// Get the current position of `value` in this solution - #[inline] - pub fn get_current_position(&self, value: &ValueOrAlias) -> Option { - self.pending.position(value).map(|index| index as u8) - } - - /// Get the current position of `value` in this solution, or panic - #[track_caller] - pub fn unwrap_current_position(&self, value: &ValueOrAlias) -> u8 { - match self.get_current_position(value) { - Some(pos) => pos, - None => panic!("value {value:?} not found on operand stack"), - } - } - - /// Returns true if the value expected at `index` is currently at that index - pub fn is_expected(&self, index: u8) -> bool { - self.get_expected(index) - .map(|v| v.eq(&self.pending[index as usize].value)) - .unwrap_or(true) - } - - /// Duplicate the operand at `index` to the top of the stack - /// - /// This records a `Copy` action, and updates the state of the stack - pub fn dup(&mut self, index: u8, alias_id: NonZeroU8) { - self.pending.dup(index as usize, alias_id); - self.actions.push(Action::Copy(index)); - } - - /// Swap the operands at `index` and the top of the stack - /// - /// This records a `Swap` action, and updates the state of the stack - pub fn swap(&mut self, index: u8) { - self.pending.swap(index as usize); - self.actions.push(Action::Swap(index)); - } - - /// Move the operand at `index` to the top of the stack - /// - /// This records a `MoveUp` action, and updates the state of the stack - #[track_caller] - pub fn movup(&mut self, index: u8) { - assert_ne!(index, 0); - if index == 1 { - self.swap(index); - } else { - self.pending.movup(index as usize); - self.actions.push(Action::MoveUp(index)); - } - } - - /// Move the operand at the top of the stack to `index` - /// - /// This records a `MoveDown` action, and updates the state of the stack - #[track_caller] - pub fn movdn(&mut self, index: u8) { - assert_ne!(index, 0); - if index == 1 { - self.swap(index); - } else { - self.pending.movdn(index as usize); - self.actions.push(Action::MoveDown(index)); - } - } - - /// Evicts the operand on top of the stack by moving it down past the last expected operand. - pub fn evict(&mut self) { - self.evict_from(0) - } - - /// Same as `evict`, but assumes that we're evicting an operand at `index` - #[inline] - pub fn evict_from(&mut self, index: u8) { - if index > 0 { - self.movup(index); - } - self.movdn(self.context.arity() as u8); - } -} diff --git a/codegen/masm/src/codegen/opt/operands/tactics/move_down_and_swap.rs b/codegen/masm/src/codegen/opt/operands/tactics/move_down_and_swap.rs deleted file mode 100644 index 33216cfea..000000000 --- a/codegen/masm/src/codegen/opt/operands/tactics/move_down_and_swap.rs +++ /dev/null @@ -1,129 +0,0 @@ -use super::*; - -/// This tactic is applied if moving the top value down the -/// stack presents an opportunity for a subsequent swap to -/// fix the order of the stack. -/// -/// The criteria for this succeeding is: -/// -/// 0. There must be no copies required, and at least two operands expected -/// 1. The value on top of the stack should either be -/// evicted, or should be moved to its expected -/// position, and if applicable, past any remaining -/// operands which belong before it in the final -/// ordering -/// 2. After moving the value on top, one of the following -/// is true: -/// * The stack is now ordered, and we're done -/// * The operand on top of the stack is out of place, -/// and the operand in its place is either: -/// 1. Expected to be on top of the stack, so we can swap -/// 2. Will be moved into place if we move the top of the -/// stack immediately past it, so we can move -/// -/// If we apply these steps, and the stack ends up in the -/// desired order, then this tactic was successful. -#[derive(Default)] -pub struct MoveDownAndSwap; -impl Tactic for MoveDownAndSwap { - fn apply(&mut self, builder: &mut SolutionBuilder) -> TacticResult { - if builder.requires_copies() || builder.arity() < 2 { - log::debug!("cannot apply tactic when there are required copies ({}) or fewer than 2 operands ({})", builder.requires_copies(), builder.arity()); - return Err(TacticError::PreconditionFailed); - } - - // If the top operand is already in position, this tactic cannot be applied - if builder.is_expected(0) { - log::debug!("abandoning tactic: operand at index 0 is already in position"); - return Err(TacticError::NotApplicable); - } - - let actual0 = builder.unwrap_current(0); - if let Some(target_pos) = builder.get_expected_position(&actual0) { - // Extend the target position past any operands which should - // come before the operand currently on top of the stack - log::trace!( - "{actual0:?} expects to be at index {target_pos}, but is on top of the stack" - ); - log::trace!("looking for operands after index {target_pos} which need to come before {actual0:?} on the stack"); - let target_offset = builder - .stack() - .iter() - .rev() - .skip(1 + target_pos as usize) - .copied() - .enumerate() - .fold(0, |acc, (offset, operand)| { - builder - .get_expected_position(&operand.value) - .and_then(|operand_expected_at| { - if target_pos >= operand_expected_at { - Some(offset + 1) - } else { - None - } - }) - .unwrap_or(acc) - }) as u8; - // Move the operand to the position we've identified - log::trace!( - "moving {actual0:?} to {}, shifting {:?} to the top of stack", - target_pos + target_offset, - builder.unwrap_current(1) - ); - builder.movdn(target_pos + target_offset); - } else { - let expected0 = builder.unwrap_expected(0); - let expected0_at = builder.unwrap_current_position(&expected0); - log::trace!("{actual0:?} is not an expected operand, but is occupying index {expected0_at}, where we expect {expected0:?}, evicting.."); - builder.evict(); - } - - // Is the item now on top of the stack misplaced? - if builder.is_expected(0) { - // If the item on top of the stack is in place, we cannot - // succeed without introducing at least one extra move. - // - // Nevertheless, we return Ok here in case we actually - // have a solution already, and also so that we can - // potentially try combining this tactic with another - // to find a solution - log::trace!("item on top of the stack is now in position, so we cannot proceed further, returning possible solution"); - return Ok(()); - } - - // Where does it belong? - let actual0 = builder.unwrap_current(0); - if let Some(target_pos) = builder.get_expected_position(&actual0) { - // Find the index where the operand at `target_pos` belongs - let target_expected_pos = - builder.get_expected_position(&builder.unwrap_current(target_pos)); - match target_expected_pos { - Some(0) => { - // The target expects to be on top, so we can swap - log::trace!("{actual0:?} is expected at {target_pos}, the occupant of which is expected on top of the stack, swapping.."); - builder.swap(target_pos); - } - Some(pos) if pos == target_pos - 1 => { - // The target would be moved into place if we move the top down - log::trace!("moving {actual0:?} to {target_pos}, the occupant of which is expected at {pos}"); - builder.movdn(target_pos); - } - Some(_) | None => { - log::trace!("unable to apply tactic, operands do not match expected pattern"); - return Err(TacticError::NotApplicable); - } - } - } else { - // We marked this operand for eviction, so moving - // it past the end of the expected operands is all - // that is needed here - let expected0 = builder.unwrap_expected(0); - let expected0_at = builder.unwrap_expected_position(&expected0); - log::trace!("{actual0:?} is not an expected operand, but is occupying index {expected0_at}, where we expect {expected0:?}, evicting.."); - builder.evict(); - } - - Ok(()) - } -} diff --git a/codegen/masm/src/codegen/opt/operands/tactics/move_up_and_swap.rs b/codegen/masm/src/codegen/opt/operands/tactics/move_up_and_swap.rs deleted file mode 100644 index 9e37dff03..000000000 --- a/codegen/masm/src/codegen/opt/operands/tactics/move_up_and_swap.rs +++ /dev/null @@ -1,146 +0,0 @@ -use super::*; - -/// This tactic is applied if moving a value to the top of the stack -/// presents an opportunity for subsequent swap to fix the order of the -/// stack. -/// -/// The criteria for this succeeding is: -/// -/// 0. There must be no copies required and at least one operand expected -/// 1. The value expected on top of the stack should be one of: -/// * On top, but the next element expected behind it is off-by-one. -/// In which case we search for the element that goes there, move -/// it up, and swap. -/// * Not on top, but in a cycle with another misplaced operand, such -/// that moving the latter to the top of the stack and swapping it -/// with the expected top operand puts them both into place -/// * Not on top, but once moved to the top, is a valid solution already -/// -/// If after performing those steps, the stack is in the correct order, -/// the tactic was successful, otherwise the operand ordering cannot be -/// solved with this tactic (alone anyway). -#[derive(Default)] -pub struct MoveUpAndSwap; -impl Tactic for MoveUpAndSwap { - fn apply(&mut self, builder: &mut SolutionBuilder) -> TacticResult { - if builder.requires_copies() || builder.arity() < 2 { - log::debug!("cannot apply tactic when there are required copies ({}) or fewer than 2 operands ({})", builder.requires_copies(), builder.arity()); - return Err(TacticError::PreconditionFailed); - } - - let expected0 = builder.unwrap_expected(0); - let actual0 = builder.unwrap_current(0); - if actual0 == expected0 { - let Some(expected1) = builder.get_expected(1) else { - log::debug!("top two operands on the stack are already in position, returning possible solution"); - return Ok(()); - }; - let move_from = builder.unwrap_current_position(&expected1); - if move_from == 1 { - log::debug!("abandoning tactic because operand at index 1 is already in position"); - return Err(TacticError::NotApplicable); - } - log::trace!("moving {expected1:?} to top of stack from index {move_from}, then swapping with {expected0:?}"); - builder.movup(move_from); - builder.swap(1); - - // The tactic was successfully applied, but it is - // up to the solver to determine if a solution was - // found - log::trace!("returning possible solution"); - return Ok(()); - } - - // Handle cases such as the following: - // - // Assume the stack is in the following order: - // - // [b, d, c, a, e] - // - // The movup + swap pattern can solve this as follows: - // - // 1. `a` is expected on top, but is down stack - // 2. If we traverse the stack between the top and `a`, - // until we find a pair of operands where the expected - // positions of the pair are not in descending order, - // we will find that `d` and `c` are such a pair. If no - // such pair is found, we consider this tactic failed. - // - // * NOTE: If the pair includes `a` itself, then we simply - // move `a` directly to the top. - // - // 3. If we move the larger of the two operands to the - // top of the stack, we will obtain the following order: - // - // [d, b, c, a, e] - // - // 4. We then identify where `a` is on the stack, and swap - // with the top operand `d`, leaving us with: - // - // [a, b, c, d, e] - let mut descending_pair = None; - let mut last_pos = None; - for operand in builder.stack().iter().rev() { - if let Some(expected_pos) = builder.get_expected_position(&operand.value) { - let current = (operand.pos, expected_pos); - let last_operand_pos = last_pos.replace(current); - if let Some(last @ (_, last_expected_pos)) = last_operand_pos { - if expected_pos >= last_expected_pos { - continue; - } - descending_pair = Some((last, current)); - break; - } - } - } - - // We found a pair of operands where the expected position of the two - // operands is in descending order, e.g. `b` before `a`. We use those - // names here to help keep track of which item is which, but keep in - // mind that we aren't implying that the pair is expected to appear - // consecutively, just that relative to one another they are out of order - if let Some((b, a)) = descending_pair { - let (b_actual, b_expected) = b; - let (a_actual, a_expected) = a; - debug_assert!(b_expected > a_expected); - // If the pair includes `a` itself, then just move `a` to the top - if a_expected == 0 { - log::trace!( - "moving {:?} to the top of stack, shifting {:?} down", - builder.stack()[a_actual as usize].value, - builder.stack()[0].value - ); - builder.movup(a_actual); - } else { - if b_actual > 0 { - log::trace!( - "moving {:?} to the top of stack, shifting {:?} down", - builder.stack()[b_actual as usize].value, - builder.stack()[0].value - ); - builder.movup(b_actual); - } - let expected0_at = builder.unwrap_current_position(&expected0); - log::trace!( - "moving {:?} to the top of stack, shifting {:?} down", - builder.stack()[expected0_at as usize].value, - builder.stack()[0].value - ); - builder.movup(expected0_at); - } - Ok(()) - } else { - // If this branch is reached, it implies that all - // of the operands on the stack are in order, - // but there is at least one unused operand to be evicted - // from the top of the stack, and possibly others that - // are interspersed between expected operands. We - // do not attempt to solve that in this tactic, and - // instead defer to MoveDownAndSwap or fallback, both - // of which focus on moving elements from the top first, - // and handle the various patterns that might arise there. - log::trace!("abandoning tactic because by implication, operands are in order, and an unused operand must need eviction"); - Err(TacticError::NotApplicable) - } - } -} diff --git a/codegen/masm/src/codegen/opt/operands/tactics/swap_and_move_up.rs b/codegen/masm/src/codegen/opt/operands/tactics/swap_and_move_up.rs deleted file mode 100644 index 4239563a0..000000000 --- a/codegen/masm/src/codegen/opt/operands/tactics/swap_and_move_up.rs +++ /dev/null @@ -1,57 +0,0 @@ -use super::*; - -/// This tactic is applied if swapping a value to the top of the stack -/// presents an opportunity for subsequent move to fix the order of the stack. -/// -/// The criteria for this succeeding is: -/// -/// 0. There must be no copies required and at least two operands expected -/// 1. The value on top of the stack should either be: -/// * Evicted, and the value at the back of the constrained stack -/// is an expected operand, thus swapping them accomplishes both -/// goals at once -/// * Swapped into its expected position, putting an operand at the -/// top which is in position, can be moved into position, or is -/// to be evicted. -/// -/// If after performing those steps, the stack is in the correct order, -/// the tactic was successful, otherwise the operand ordering cannot be -/// solved with this tactic (alone anyway). -#[derive(Default)] -pub struct SwapAndMoveUp; -impl Tactic for SwapAndMoveUp { - fn apply(&mut self, builder: &mut SolutionBuilder) -> TacticResult { - if builder.requires_copies() || builder.arity() < 2 { - log::debug!("cannot apply tactic when there are required copies ({}) or fewer than 2 operands ({})", builder.requires_copies(), builder.arity()); - return Err(TacticError::PreconditionFailed); - } - - // Find the operand that should be at index 1 and swap the top element - // with it; then move up the value that should be at index 0 - let Some(expected1) = builder.get_expected(1) else { - log::debug!("abandoning tactic because operand at index 1 is already in position"); - return Err(TacticError::NotApplicable); - }; - let expected1_pos = builder.unwrap_current_position(&expected1); - if expected1_pos == 0 { - log::trace!( - "swapping {expected1:?} from top of the stack, with {:?} at index 1", - builder.stack()[1].value - ); - builder.swap(1); - } else { - log::trace!("swapping {expected1:?} at index {expected1_pos} to the top of the stack, with {:?}", builder.stack()[0].value); - builder.swap(expected1_pos); - } - - // Find the operand that should be at index 0 and move it into position - let expected0 = builder.unwrap_expected(0); - let expected0_pos = builder.unwrap_current_position(&expected0); - if expected0_pos > 0 { - log::trace!("moving {expected0:?} from index {expected0_pos} to the top of stack, shifting {:?} down by one", builder.stack()[0].value); - builder.movup(expected0_pos); - } - - Ok(()) - } -} diff --git a/codegen/masm/src/codegen/scheduler.rs b/codegen/masm/src/codegen/scheduler.rs deleted file mode 100644 index 108bbdfd0..000000000 --- a/codegen/masm/src/codegen/scheduler.rs +++ /dev/null @@ -1,1394 +0,0 @@ -use std::cmp::Ordering; -use std::collections::VecDeque; -use std::rc::Rc; - -use cranelift_entity::SecondaryMap; -use miden_hir::{ - self as hir, - adt::{SmallMap, SmallSet, SparseMap, SparseMapValue}, - assert_matches, BranchInfo, ProgramPoint, -}; -use miden_hir_analysis::{ - dependency_graph::{ArgumentNode, DependencyGraph, Node, NodeId}, - DominatorTree, LivenessAnalysis, Loop, LoopAnalysis, OrderedTreeGraph, -}; - -use smallvec::SmallVec; - -use crate::codegen::Constraint; -use crate::masm; - -/// Information about a block's successor -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Successor { - pub block: hir::Block, - pub arg_count: u16, -} - -#[derive(Debug)] -pub struct ValueInfo { - /// The value in question - pub value: hir::Value, - /// The node corresponding to this value (i.e. a Result or Stack node) - pub node: NodeId, - /// If the value is not explicitly used, but is live after the current - /// block, it is considered used for purposes of dead code analysis - pub is_externally_used: bool, - /// The instruction nodes in the dependency graph which use this value. - /// - /// This vector is sorted such that the earlier a user appears in it, - /// the later they are scheduled in the block. - users: SmallVec<[NodeId; 1]>, -} -impl ValueInfo { - pub fn is_used(&self) -> bool { - self.is_externally_used || !self.users.is_empty() - } - - /// Return the [NodeId] of the first user of this value to be emitted - pub fn first_user(&self) -> Option { - self.users.last().copied() - } - - /// Return the [NodeId] of the last user of this value to be emitted - #[allow(unused)] - pub fn last_user(&self) -> Option { - self.users.first().copied() - } -} - -/// Represents important metadata about an instruction used -/// to schedule its execution and data/control dependencies. -#[derive(Debug)] -pub struct InstInfo { - /// The id of the instruction - pub inst: hir::Inst, - /// The node id of this instruction - pub node: NodeId, - /// The number of plain arguments this instruction expects - pub arity: u8, - /// Both plain arguments and block arguments are stored in this - /// vector; plain arguments always come first, and block arguments - /// start immediately after the last plain argument. - /// - /// The arguments for each block are stored consecutively based on - /// the order of the successors in the instruction. So if there is - /// one plain argument, and two blocks with two arguments each, then - /// the layout of all arguments will be: - /// - /// ```text,ignore - /// [plain, block0_arg0, block0_arg1, block1_arg0, block1_arg1] - /// ``` - pub args: SmallVec<[Constraint; 4]>, - /// Information about the values produced by this instruction as results - /// - /// This vector is sorted by the first use of each result, such that the - /// earlier a value appears in it, the earlier that value is used in - /// the scheduled block. - pub results: SmallVec<[ValueInfo; 2]>, - /// The set of dependencies which must be scheduled before - /// the instruction starts executing - /// - /// This set is populated in argument order - pub pre: SmallSet, - /// The set of dependencies which must be scheduled after - /// the instruction finishes executing. - /// - /// This is largely relevant only for control flow instructions, - /// particularly those such as `cond_br`, which may push down - /// materialization of certain data dependencies into the control - /// flow edge itself, rather than before the instruction starts - /// execution. This can avoid unnecessarily executing instructions - /// that aren't ultimately used due to a runtime condition. - pub post: SmallSet, - /// The successor blocks and argument count for this instruction - pub successors: SmallVec<[Successor; 2]>, -} -impl SparseMapValue for InstInfo { - fn key(&self) -> hir::Inst { - self.inst - } -} -impl InstInfo { - #[inline] - pub const fn arity(&self) -> usize { - self.arity as usize - } - - /// Get the constraints for the plain arguments of this instruction - pub fn plain_arguments(&self) -> &[Constraint] { - &self.args[..self.arity()] - } - - /// Get the constraints for the arguments of successor `block` - pub fn block_arguments(&self, block: hir::Block) -> &[Constraint] { - let range = self.block_argv_range(block); - &self.args[range] - } - - fn block_argv_range(&self, block: hir::Block) -> core::ops::Range { - let mut start_idx = self.arity(); - let mut arg_count = 0; - for successor in self.successors.iter() { - if successor.block == block { - arg_count = successor.arg_count as usize; - break; - } - start_idx += successor.arg_count as usize; - } - start_idx..(start_idx + arg_count) - } -} - -/// [BlockInfo] describes information about a block relevant to scheduling -/// and code generation. Namely, it provides access to the computed dependency -/// graph and tree graph used to schedule the block; but it also provides -/// convenient access to commonly-queried block information. -#[derive(Debug)] -pub struct BlockInfo { - /// The source HIR block this info is based on - 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, - /// The dependency graph of this block - pub depgraph: DependencyGraph, - /// The topologically-ordered tree graph of this block - pub treegraph: OrderedTreeGraph, -} -impl BlockInfo { - #[inline(always)] - pub fn is_loop_header(&self) -> bool { - self.loop_header.is_some() - } -} -impl SparseMapValue for BlockInfo { - fn key(&self) -> hir::Block { - self.source - } -} - -/// [Schedule] describes an instruction scheduling plan for a single IR function. -/// -/// The plan describes the order in which blocks will be scheduled, -/// and the schedule for instructions in each block. -#[derive(Debug)] -pub struct Schedule { - pub block_infos: SparseMap>, - pub block_schedules: SecondaryMap>, -} -impl Schedule { - pub fn new() -> Self { - Self { - block_infos: Default::default(), - block_schedules: SecondaryMap::new(), - } - } - - #[inline] - pub fn block_info(&self, block: hir::Block) -> Rc { - self.block_infos.get(block).cloned().unwrap() - } - - #[inline] - pub fn get(&self, block: hir::Block) -> &[ScheduleOp] { - self.block_schedules[block].as_slice() - } -} - -/// [ScheduleOp] describes an action to be performed by the code generator. -/// -/// Code generation is driven by constructing a schedule consisting of these operations -/// in a specific order that represents a valid execution of the original IR, and -/// then traversing the schedule start-to-finish, translating these high-level operations -/// into equivalent Miden Assembly code. By performing code generation in this manner, -/// we are able to see a high-level description of how the compiler plans to generate -/// code for a function, and we also enable the code generator to make last-minute -/// optimizations based on the local context of each [ScheduleOp], much like a peephole -/// 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 occurrance of the given value on the operand stack - Drop(hir::Value), -} - -/// Meta-instruction for the abstract scheduling machine used -/// to emit an instruction schedule for a function. -#[derive(Debug)] -pub enum Plan { - /// Start a new block context - /// - /// 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), - /// Schedule execution of an instruction's pre-requisites - PreInst(Rc), - /// Schedule execution of the given instruction - Inst(Rc), - /// Schedule execution of any instruction cleanup - /// - /// This is primarily intended to support things like pushing materialization - /// of data dependencies down along control flow edges when they are conditionally - /// required, but can also be used to schedule instruction cleanup/etc. - PostInst(Rc), - /// Schedule an unused instruction result to be dropped from the operand stack - Drop(hir::Value), - /// Indicate that the current block context should be popped for the previous - /// one on the block stack - Finish, -} - -pub struct Scheduler<'a> { - f: &'a hir::Function, - f_prime: &'a mut masm::Function, - domtree: &'a DominatorTree, - loops: &'a LoopAnalysis, - liveness: &'a LivenessAnalysis, - schedule: Schedule, -} -impl<'a> Scheduler<'a> { - pub fn new( - f: &'a hir::Function, - f_prime: &'a mut masm::Function, - domtree: &'a DominatorTree, - loops: &'a LoopAnalysis, - liveness: &'a LivenessAnalysis, - ) -> Self { - Self { - f, - f_prime, - domtree, - loops, - liveness, - schedule: Schedule::new(), - } - } - - 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)]), - }; - block_scheduler.schedule(schedule); - } - - self.schedule - } - - fn precompute_block_infos(&mut self) { - let entry_block_id = self.f.dfg.entry_block(); - - for block_id in self.domtree.cfg_postorder().iter().rev().copied() { - // Ensure we have a target block for each source IR block being scheduled - let masm_block_id = if block_id == entry_block_id { - self.f_prime.body.id() - } else { - self.f_prime.create_block() - }; - - // 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"); - - let info = Rc::new(BlockInfo { - source: block_id, - target: masm_block_id, - last_inst, - innermost_loop, - loop_header, - depgraph, - treegraph, - }); - - self.schedule.block_infos.insert(info); - } - } -} - -struct BlockScheduler<'a> { - f: &'a hir::Function, - liveness: &'a LivenessAnalysis, - block_info: Rc, - inst_infos: SparseMap>, - worklist: SmallVec<[Plan; 4]>, -} -impl<'a> BlockScheduler<'a> { - pub fn schedule(mut self, scheduled_ops: &mut Vec) { - // Planning tasks are added to the worklist in reverse, e.g. we push - // Plan::Finish before Plan::Start. So by popping tasks off the stack - // 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); - } - // 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 the results of an instruction that has not been - // scheduled yet - Plan::PreInst(inst_info) => self.schedule_pre_inst(inst_info), - // We're emitting code for an instruction whose pre-requisite dependencies are already - // materialized, so we need only worry about how a specific instruction is lowered. - Plan::Inst(inst_info) => self.schedule_inst(inst_info, scheduled_ops), - // We're emitting code for an instruction that has started executing, and in some specific - // cases, may have dependencies which have been deferred until this point. This is only emitted - // currently for block arguments which are conditionally materialized - Plan::PostInst(inst_info) => self.schedule_post_inst(inst_info), - Plan::Drop(value) => { - scheduled_ops.push(ScheduleOp::Drop(value)); - } - } - } - } - - /// Visit the current block and enqueue planning operations to drive scheduling - fn visit_block(&mut self) { - // When all scheduling meta-ops are complete for this block, this instruction - // will switch context back to the parent block in the stack - self.worklist.push(Plan::Finish); - - // The treegraph iterator visits each node before any of that node's successors, - // i.e. dependencies. As a result, all code that emits planning tasks (i.e. Plan), - // must be written in the order opposite of the resulting scheduling tasks (i.e. ScheduleOp). - // - // This is critical to internalize, because reasoning about dependencies and when things - // are live/dead is _inverted_ here. You must ensure that when planning things based on - // dependency order or liveness, that you do so taking this workflow inversion into account. - // - // The actual scheduling tasks are handled in the proper program execution order, but we - // have to handle this inversion somewhere, and planning seemed like the best place, since - // it is relatively limited in size and scope. - // - // The goal here is to produce a stack of scheduling instructions that when evaluated, - // will emit code in the correct program order, i.e. instructions which produce results - // used by another instruction will be emitted so that those results are available on - // the operand stack when we lower the instruction, and we need only copy/move those - // operands into place. - let current_block_info = self.block_info.clone(); - for node_id in current_block_info.treegraph.iter() { - match node_id.into() { - // Result nodes are treegraph roots by two paths: - // - // * The result is used multiple times. Here, in the planning phase, we must have just - // finished visiting all of its dependents, so to ensure that during scheduling the - // result is materialized before its first use, we must do so here. If this result is - // one of many produced by the same instruction, we must determine if this is the first - // result to be seen, or the last. The last result to be visited during planning is the - // one that will actually materialize all of the results for the corresponding instruction. - // Thus, if this is not the last result visited. - // - // * The result is never used, in which case, like above, we must determine if this result - // should force materialization of the instruction. The caveat here is that if this is the - // only result produced by its instruction, and it is not live after the end of the current - // block, then we will avoid materializing at all if the instruction has no side effects. - // If it _does_ have side effects, then we will force materialization of the result, but - // then schedule it to be immediately dropped - Node::Result { value, .. } => { - self.maybe_force_materialize_inst_results(value, node_id) - } - // During the planning phase, there is only one useful thing to do with this node type, - // which is to determine if it has any dependents in the current block, and if not, - // schedule a drop of the value if liveness analysis tells us that the value is not - // used after the current block. - Node::Stack(value) => { - // If this value is live after the end of this block, it cannot be dropped - if self - .liveness - .is_live_after(&value, ProgramPoint::Block(current_block_info.source)) - { - continue; - } - // If this value is used within this block, then the last use will consume it - if current_block_info.treegraph.num_predecessors(node_id) > 0 { - continue; - } - // Otherwise, we must ensure it gets dropped, so do so immediately - self.worklist.push(Plan::Drop(value)); - } - // We will only ever observe the instruction node type as a treegraph root - // when it has no results (and thus no dependents/predecessors in the graph), - // because in all other cases it will always have a predecessor of Result type. - // - // In practice, we only observe these nodes when handling block terminators. - Node::Inst { id: inst, .. } => { - let inst_info = self.get_or_analyze_inst_info(inst, node_id); - self.plan_inst(inst_info); - } - // It can never be the case that argument nodes are unused or multiply-used, - // they will always be successors of an Inst node - Node::Argument(_) => unreachable!(), - } - } - } - - fn plan_inst(&mut self, inst_info: Rc) { - // Only push an item for post-inst scheduling if we have something to do - if !inst_info.post.is_empty() { - // This meta-op will emit code that is required along the control flow edge - // represented by the branch. - self.worklist.push(Plan::PostInst(inst_info.clone())); - } - // Only push an item for pre-inst scheduling if we have something to do - if inst_info.pre.is_empty() { - // This meta-op will emit code for the unconditional branch - self.worklist.push(Plan::Inst(inst_info)); - } else { - // This meta-op will emit code for the unconditional branch - self.worklist.push(Plan::Inst(inst_info.clone())); - // This meta-op will emit code that is required to evaluate the instruction - // - // It is also responsible for scheduling dependencies - self.worklist.push(Plan::PreInst(inst_info)); - } - } - - /// Schedule pre-requisites for an instruction based on the given analysis, see [Plan::PreInst] docs for more. - /// - /// This function, while nominally occuring during the scheduling phase, actually emits planning - /// tasks which are processed _before_ the [Plan::Inst] task corresponding to `inst_info`. As a - /// result, we visit the pre-requisites in planning order, _not_ execution order. - fn schedule_pre_inst(&mut self, inst_info: Rc) { - // Schedule dependencies for execution in the order that they must execute - if inst_info - .pre - .as_slice() - .is_sorted_by(|a, b| Some(self.block_info.treegraph.cmp_scheduling(*a, *b))) - { - self.schedule_inst_dependencies(&inst_info, inst_info.pre.as_slice()); - } else { - let mut deps = inst_info.pre.clone().into_vec(); - deps.sort_by(|a, b| self.block_info.treegraph.cmp_scheduling(*a, *b)); - self.schedule_inst_dependencies(&inst_info, deps.as_slice()); - } - } - - fn schedule_inst_dependencies(&mut self, inst_info: &InstInfo, dependencies: &[NodeId]) { - for dependency_id in dependencies.iter().copied() { - // If the dependency is a treegraph root, we do not need to do anything, - // as that dependency will be scheduled at a more appropriate time prior - // the code generated here, and the dependency will be available on the - // operand stack - if self.block_info.treegraph.is_root(dependency_id) { - continue; - } - - // Otherwise, dispatch based on the node type of the dependency - match dependency_id.into() { - // We are the only user of this result, otherwise it would be a - // treegraph root. As a result, we simply need to determine whether - // or not the instruction which produces it must be materialized by - // us, or via another result - Node::Result { value, .. } => { - self.maybe_materialize_inst_results(value, dependency_id, inst_info) - } - // We avoid adding these nodes as pre-requisites as they are assumed to - // be on the operand stack already, but we handle it gracefully here anyway - Node::Stack(_) => continue, - // This is a control dependency, so it must be materialized - // - // Currently, control dependencies are only attached to a single instruction, - // so we do not need to check if another dependent will materialize it, it is - // definitely on us. - Node::Inst { id: inst, .. } => { - let inst_info = self.get_or_analyze_inst_info(inst, dependency_id); - self.materialize_inst_results(inst_info); - } - // This node type is never added as a pre-requisite - Node::Argument(_) => unreachable!(), - } - } - } - - /// Schedule execution of a given instruction, see [Plan::Inst] docs for specific semantics. - fn schedule_inst(&mut self, inst_info: Rc, scheduled_ops: &mut Vec) { - scheduled_ops.push(ScheduleOp::Inst(inst_info)); - } - - /// Schedule instructions which were deferred until after an instruction executes. - /// - /// See [Plan::PostInst] docs for more. - fn schedule_post_inst(&mut self, inst_info: Rc) { - todo!("post-execution instruction dependencies are not supported yet: {inst_info:?}"); - } - - /// We are visiting a `Node::Result` during planning, and need to determine whether or - /// not to materialize the result at this point, or if we must defer to another result of - /// the same instruction which is visited later in planning. - /// - /// The name of this function refers to the fact that we may have to "force" materialization - /// of an instruction when the Result node has no predecessors in the graph, but is either - /// live _after_ the current block, or its instruction has side effects that require it to - /// be materialized anyway. - fn maybe_force_materialize_inst_results(&mut self, result: hir::Value, result_node: NodeId) { - let inst_node = self.block_info.depgraph.unwrap_child(result_node); - let inst = inst_node.unwrap_inst(); - debug_assert_eq!(inst, self.f.dfg.value_data(result).unwrap_inst()); - let inst_info = self.get_or_analyze_inst_info(inst, inst_node); - - // Do not force materialization because the first result scheduled is responsible for that - let is_first_result_used = inst_info.results.first().unwrap().node == result_node; - if !is_first_result_used { - return; - } - - // We're the first result scheduled, whether used or not. If the result is - // not actually used, we may need to force materialization if the following - // two conditions hold: - // - // * There are no results used - // * The instruction has side effects - // - // However, if any of the results are used, we must materialize them now, since - // we are scheduled before any of the others. - let is_used = inst_info.results.iter().any(|v| v.is_used()); - let has_side_effects = self.f.dfg.inst(inst).has_side_effects(); - - if is_used || has_side_effects { - self.materialize_inst_results(inst_info); - } - } - - fn maybe_materialize_inst_results( - &mut self, - result: hir::Value, - result_node: NodeId, - dependent_info: &InstInfo, - ) { - let inst_node = self.block_info.depgraph.unwrap_child(result_node); - let inst = inst_node.unwrap_inst(); - debug_assert_eq!(inst, self.f.dfg.value_data(result).unwrap_inst()); - let inst_info = self.get_or_analyze_inst_info(inst, inst_node); - - // We must materialize the instruction the first time it is referenced - let is_first_result_used = inst_info.results.first().unwrap().node == result_node; - // If this is not the first result of the referenced instruction to be - // scheduled, then the instruction results are already materialized - if !is_first_result_used { - return; - } - - // If this result is the first one to be used, but we are not the first user, - // we also do nothing, since the first user materializes - let first_user = inst_info.results[0].first_user().unwrap(); - let is_first_use = first_user == dependent_info.node; - if !is_first_use { - return; - } - - // We're the first use of the referenced instruction, so materialize its - // results, and drop any that have no uses. - self.materialize_inst_results(inst_info); - } - - fn materialize_inst_results(&mut self, inst_info: Rc) { - let inst_results = self.f.dfg.inst_results(inst_info.inst); - for result in inst_results.iter().copied() { - let is_used = inst_info - .results - .iter() - .any(|v| v.value == result && v.is_used()); - if !is_used { - self.worklist.push(Plan::Drop(result)); - } - } - self.plan_inst(inst_info); - } - - /// Get the analysis for `inst`, or perform the analysis now and cache it for future queries - fn get_or_analyze_inst_info(&mut self, inst: hir::Inst, inst_node_id: NodeId) -> Rc { - match self.inst_infos.get(inst).cloned() { - Some(info) => info, - None => { - let info = self.analyze_inst(inst, inst_node_id); - self.inst_infos.insert(info.clone()); - info - } - } - } - - /// Analyze an instruction node from the current block's dependency graph. - /// - /// This analysis produces an [InstInfo] that is used during scheduling and - /// code generation. It provides information about how dependencies of the instruction - /// should be scheduled, and whether to copy or move data dependencies when needed, - /// along with some commonly requested pieces of information about the instruction, - /// such as the number of arguments, its successors, etc. - /// - /// NOTE: This can be called either during planning or scheduling, but the - /// analysis is always scheduling-oriented. - fn analyze_inst(&self, inst: hir::Inst, inst_node_id: NodeId) -> Rc { - let inst_args = self.f.dfg.inst_args(inst); - let arity = inst_args.len() as u8; - - let results = self.analyze_inst_results(inst); - let mut inst_info = Box::new(InstInfo { - inst, - node: inst_node_id, - arity, - args: Default::default(), - results, - pre: Default::default(), - post: Default::default(), - successors: Default::default(), - }); - - match self.f.dfg.analyze_branch(inst) { - BranchInfo::SingleDest(block, block_args) => { - inst_info.successors.push(Successor { - block, - arg_count: block_args.len() as u16, - }); - for (succ_idx, arg) in self - .block_info - .depgraph - .successors(inst_node_id) - .filter(|succ| succ.dependency.is_argument()) - .enumerate() - { - let arg_id = arg.dependency; - let arg_source_id = self.block_info.depgraph.unwrap_child(arg_id); - if !arg_source_id.is_stack() { - inst_info.pre.insert(arg_source_id); - } - match arg.dependency.into() { - Node::Argument(ArgumentNode::Direct { index, .. }) => { - debug_assert_eq!( - succ_idx, index as usize, - "successor ordering constraint violation: {arg:?}" - ); - inst_info.args.push(self.constraint( - inst_args[index as usize], - arg_id, - arg_source_id, - None, - )); - } - Node::Argument(ArgumentNode::Indirect { index, .. }) => { - debug_assert_eq!( - succ_idx + inst_args.len(), - index as usize, - "successor ordering constraint violation: {arg:?}" - ); - let jt = hir::JumpTable { - destination: block, - args: block_args, - }; - inst_info.args.push(self.constraint( - block_args[index as usize], - arg_id, - arg_source_id, - Some(&[jt]), - )); - } - Node::Argument(arg) => { - panic!("invalid argument type for single-destination branch: {arg:?}") - } - _ => unreachable!(), - } - } - } - BranchInfo::MultiDest(ref jts) => { - for jt in jts.iter() { - inst_info.successors.push(Successor { - block: jt.destination, - arg_count: jt.args.len() as u16, - }); - } - for (succ_idx, arg) in self - .block_info - .depgraph - .successors(inst_node_id) - .filter(|succ| succ.dependency.is_argument()) - .enumerate() - { - let arg_id = arg.dependency; - let arg_source_id = self.block_info.depgraph.unwrap_child(arg_id); - match arg.dependency.into() { - Node::Argument(ArgumentNode::Direct { index, .. }) => { - debug_assert_eq!( - succ_idx, index as usize, - "successor ordering constraint violation: {arg:?}" - ); - if !arg_source_id.is_stack() { - inst_info.pre.insert(arg_source_id); - } - inst_info.args.push(self.constraint( - inst_args[index as usize], - arg_id, - arg_source_id, - Some(jts), - )); - } - Node::Argument(ArgumentNode::Indirect { - successor, index, .. - }) => { - debug_assert_eq!( - succ_idx - - inst_args.len() - - jts[..(successor as usize)] - .iter() - .map(|jt| jt.args.len()) - .sum::(), - index as usize, - "successor ordering constraint violation: {arg:?}" - ); - if !arg_source_id.is_stack() { - inst_info.pre.insert(arg_source_id); - } - let block_arg = jts[successor as usize].args[index as usize]; - inst_info.args.push(self.constraint( - block_arg, - arg_id, - arg_source_id, - Some(jts), - )); - } - Node::Argument(ArgumentNode::Conditional { - successor, index, .. - }) => { - debug_assert_eq!( - succ_idx - - inst_args.len() - - jts[..(successor as usize)] - .iter() - .map(|jt| jt.args.len()) - .sum::(), - index as usize, - "successor ordering constraint violation: {arg:?}" - ); - if !arg_source_id.is_stack() { - // TODO: We need to figure out a better way to avoid - // materializing conditionally-required results. For - // now, we treat these like regular argument dependencies - // so that they are on the operand stack when needed. - //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]; - inst_info.args.push(self.constraint( - block_arg, - arg_id, - arg_source_id, - Some(jts), - )); - } - _ => unreachable!(), - } - } - } - BranchInfo::NotABranch => { - for (succ_idx, arg) in self - .block_info - .depgraph - .successors(inst_node_id) - .filter(|succ| succ.dependency.is_argument()) - .enumerate() - { - let arg_id = arg.dependency; - let arg_source_id = self.block_info.depgraph.unwrap_child(arg_id); - match arg.dependency.into() { - Node::Argument(ArgumentNode::Direct { index, .. }) => { - debug_assert_eq!( - succ_idx, index as usize, - "successor ordering constraint violation: {arg:?}" - ); - if !arg_source_id.is_stack() { - inst_info.pre.insert(arg_source_id); - } - inst_info.args.push(self.constraint( - inst_args[index as usize], - arg_id, - arg_source_id, - None, - )); - } - Node::Argument(arg) => { - panic!("invalid argument type for non-branching instruction: {arg:?}") - } - _ => unreachable!(), - } - } - } - } - - // Add any control dependencies after the argument dependencies to - // ensure that any results they produce do not interfere with the - // placement of operands on the stack (to the degree possible). - for succ in self - .block_info - .depgraph - .successors(inst_node_id) - .filter(|succ| !succ.dependency.is_argument()) - { - let succ_node_id = if succ.dependency.is_instruction() { - succ.dependency - } else { - assert!(succ.dependency.is_result()); - self.block_info.depgraph.unwrap_child(succ.dependency) - }; - inst_info.pre.insert(succ_node_id); - } - - Rc::from(inst_info) - } - - /// Analyze the liveness/usage of the results produced by `inst`, and determine - /// the order that they will be scheduled in. - fn analyze_inst_results(&self, inst: hir::Inst) -> SmallVec<[ValueInfo; 2]> { - let mut infos = SmallVec::<[ValueInfo; 2]>::default(); - // NOTE: Instruction results are presumed to appear on the stack in - // the same order as produced by the instruction. - for (result_idx, value) in self.f.dfg.inst_results(inst).iter().copied().enumerate() { - let result_node = Node::Result { - value, - index: result_idx as u8, - }; - let result_node_id = result_node.id(); - - // A result is "externally used" if it is live after the current block - let is_externally_used = self - .liveness - .is_live_after(&value, ProgramPoint::Block(self.block_info.source)); - - let mut info = ValueInfo { - value, - node: result_node_id, - is_externally_used, - users: Default::default(), - }; - - // Record all of the instructions in the current block which use this result - for pred in self.block_info.depgraph.predecessors(result_node_id) { - if pred.dependent.is_argument() { - info.users - .push(self.block_info.depgraph.unwrap_parent(pred.dependent)); - } else { - assert!(pred.dependent.is_instruction()); - info.users.push(pred.dependent); - } - } - - // Sort users in scheduling order, i.e. the earlier they appear in the - // list, the earlier they are visited during planning, and thus the later - // they are scheduled in the block during codegen. - info.users - .sort_unstable_by(|a, b| self.block_info.treegraph.cmp_scheduling(*a, *b)); - infos.push(info); - } - - // Sort the results by the scheduling order of their first use, i.e. the earlier - // they appear in the list, the later they are visited during planning, and - // thus the earlier they are actually used. - infos.sort_unstable_by(|a, b| match (a.first_user(), b.first_user()) { - (None, None) => Ordering::Equal, - (None, Some(_)) => Ordering::Greater, - (Some(_), None) => Ordering::Less, - (Some(a), Some(b)) => self.block_info.treegraph.cmp_scheduling(a, b).reverse(), - }); - infos - } - - /// Analyze `arg` and its usage at the current program point to determine what - /// type of operand constraint applies to it, i.e. whether the corresponding - /// operand can be moved/consumed, or must be copied first. - /// - /// An operand must be copied unless all of the following are true: - /// - /// * This is the last use (in the current block) of the value - /// * The value is not live in any successor, with the exception of - /// successors which define the value (as seen along loopback edges) - /// - /// If both of those properties hold, then the operand can be consumed directly. - fn constraint( - &self, - arg: hir::Value, - arg_node: NodeId, - arg_sourced_from: NodeId, - successors: Option<&[hir::JumpTable<'_>]>, - ) -> Constraint { - if cfg!(debug_assertions) { - assert_matches!( - arg_sourced_from.into(), - Node::Stack(_) | Node::Result { .. }, - "unexpected argument source: {:?}", - arg_sourced_from - ); - } - let mut transitive_dependents = - transitive_instruction_dependents(arg_sourced_from, &self.block_info); - let this_dependent = self.block_info.depgraph.unwrap_parent(arg_node); - transitive_dependents.remove(&this_dependent); - let is_last_dependent = transitive_dependents.iter().copied().all(|td| { - self.block_info - .treegraph - .is_scheduled_after(td, this_dependent) - }); - let is_live_after = match arg_node.into() { - Node::Argument(ArgumentNode::Direct { .. }) => successors - .map(|jts| { - jts.iter().any(|jt| { - let defined_by = self.f.dfg.block_args(jt.destination).contains(&arg); - let is_live_at = self - .liveness - .is_live_at(&arg, ProgramPoint::Block(jt.destination)); - is_live_at && !defined_by - }) - }) - .unwrap_or_else(|| { - self.liveness - .is_live_after(&arg, ProgramPoint::Block(self.block_info.source)) - }), - Node::Argument(ArgumentNode::Indirect { successor, .. }) - | Node::Argument(ArgumentNode::Conditional { successor, .. }) => { - let successors = successors.unwrap(); - let successor = successors[successor as usize].destination; - let defined_by = self.f.dfg.block_args(successor).contains(&arg); - let is_live_at = self - .liveness - .is_live_at(&arg, ProgramPoint::Block(successor)); - is_live_at && !defined_by - } - _ => unreachable!(), - }; - let is_last_use = !is_live_after && is_last_dependent; - if is_last_use { - Constraint::Move - } else { - Constraint::Copy - } - } -} - -fn transitive_instruction_dependents( - stack_or_result_node: NodeId, - current_block_info: &BlockInfo, -) -> SmallSet { - current_block_info - .depgraph - .predecessors(stack_or_result_node) - .map(|p| { - assert!(p.dependent.is_argument()); - current_block_info.depgraph.unwrap_parent(p.dependent) - }) - .collect() -} - -fn build_dependency_graph( - block_id: hir::Block, - function: &hir::Function, - liveness: &LivenessAnalysis, -) -> DependencyGraph { - let mut graph = DependencyGraph::default(); - - // This set represents the values which are guaranteed to be materialized for an instruction - let mut materialized_args = SmallSet::::default(); - // This map represents values used as block arguments, and the successors which use them - let mut block_arg_uses = SmallMap::>::default(); - - // For each instruction, record it and it's arguments/results in the graph - for (inst_index, inst) in function.dfg.block_insts(block_id).enumerate() { - materialized_args.clear(); - block_arg_uses.clear(); - - let node_id = graph.add_node(Node::Inst { - id: inst, - pos: inst_index as u16, - }); - - let pp = ProgramPoint::Inst(inst); - for (arg_idx, arg) in function.dfg.inst_args(inst).iter().copied().enumerate() { - materialized_args.insert(arg); - let arg_node = ArgumentNode::Direct { - inst, - index: arg_idx.try_into().expect("too many arguments"), - }; - graph.add_data_dependency(node_id, arg_node, arg, pp, function); - } - - // Ensure all result nodes are added to the graph, otherwise unused results will not be - // present in the graph which will cause problems when we check for those results later - for (result_idx, result) in function.dfg.inst_results(inst).iter().copied().enumerate() { - let result_node = Node::Result { - value: result, - index: result_idx as u8, - }; - let result_node_id = graph.add_node(result_node); - graph.add_dependency(result_node_id, node_id); - } - - match function.dfg.analyze_branch(inst) { - BranchInfo::SingleDest(_, args) => { - // Add edges representing these data dependencies in later blocks - for (arg_idx, arg) in args.iter().copied().enumerate() { - let arg_node = ArgumentNode::Indirect { - inst, - index: arg_idx.try_into().expect("too many successor arguments"), - successor: 0, - }; - graph.add_data_dependency(node_id, arg_node, arg, pp, function); - } - } - BranchInfo::MultiDest(ref jts) => { - // Preprocess the arguments which are used so we can determine materialization requirements - for jt in jts.iter() { - for arg in jt.args.iter().copied() { - block_arg_uses - .entry(arg) - .or_insert_with(Default::default) - .insert(jt.destination); - } - } - // For each successor, check if we should implicitly require an argument along that edge due - // to liveness analysis indicating that it is used 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(); - // 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() { - let is_conditionally_materialized = - block_arg_uses[&arg].len() < materialization_threshold; - let must_materialize = - materialized_args.contains(&arg) || !is_conditionally_materialized; - let index = arg_idx.try_into().expect("too many successor arguments"); - let successor = succ_idx.try_into().expect("too many successors"); - let arg_node = if must_materialize { - ArgumentNode::Indirect { - inst, - index, - successor, - } - } else { - ArgumentNode::Conditional { - inst, - index, - successor, - } - }; - graph.add_data_dependency(node_id, arg_node, arg, pp, function); - } - } - } - BranchInfo::NotABranch => (), - } - } - - // HACK: If there are any instruction nodes with no predecessors, with the exception of the block terminator, - // then we must add a control dependency to the graph to reflect the fact that the instruction - // must have been placed in this block intentionally. However, we are free to schedule the instruction - // as we see fit to avoid de-optimizing the normal instruction schedule unintentionally. - // - // We also avoid adding control dependencies for instructions without side effects that are not live - // beyond the current block, as those are dead code and should be eliminated in the DCE step. - // - // The actual scheduling decision for the instruction is deferred to `analyze_inst`, where we - // treat the instruction similarly to argument materialization, and either make it a pre-requisite - // of the instruction or execute it in the post-execution phase depending on the terminator type - assign_control_dependencies(&mut graph, block_id, function, liveness); - - // Eliminate dead code as indicated by the state of the dependency graph - dce(&mut graph, block_id, function, liveness); - - graph -} - -/// Discover any instructions in the given block that have no predecessors, but that must be scheduled -/// anyway, i.e. due to side effects - and make the block terminator dependent on them to ensure that -/// they are scheduled. -/// -/// We call these instruction->instruction dependencies "control dependencies", since control flow in the -/// block depends on them being executed first. In a way these dependencies are control-flow sensitive, but -/// because the instruction has no direct predecessors, we assume that we are free to schedule them anywhere -/// in the block. For the time being, we choose to schedule them just prior to leaving the block, but in the -/// future we may wish to do more intelligent scheduling of these items, either to reduce the live ranges of -/// values which are used as instruction operands, or if we find that we must attempt to more faithfully -/// preserve the original program ordering for some reason. -/// -/// NOTE: This function only assigns control dependencies for instructions _with_ side effects. An -/// instruction with no dependents, and no side effects, is treated as dead code, since by definition -/// its effects cannot be visible. It should be noted however that we are quite conservative about -/// determining if an instruction has side effects - e.g., all function calls are assumed to have -/// side effects at this point in time. -fn assign_control_dependencies( - graph: &mut DependencyGraph, - block_id: hir::Block, - function: &hir::Function, - liveness: &LivenessAnalysis, -) { - let terminator = { - let block = function.dfg.block(block_id); - let id = block.last().unwrap(); - Node::Inst { - id, - pos: (block.len() - 1) as u16, - } - }; - let terminator_id = terminator.into(); - for (inst_index, inst) in function.dfg.block_insts(block_id).enumerate() { - let opcode = function.dfg.inst(inst).opcode(); - // Skip the block terminator - if opcode.is_terminator() { - continue; - } - - let node = Node::Inst { - id: inst, - pos: inst_index as u16, - }; - let node_id = node.id(); - - // Skip instructions with transitive dependents on at least one result, or a direct dependent - let has_dependents = graph.predecessors(node_id).any(|pred| { - if pred.dependent.is_result() { - graph.num_predecessors(pred.dependent) > 0 - } else { - true - } - }); - if has_dependents { - continue; - } - - // Instructions with no side effects require a control dependency if at least - // one result is live after the end of the current block. We add the dependency - // to the instruction results if present, otherwise to the instruction itself. - let pp = ProgramPoint::Block(block_id); - let mut live_results = SmallVec::<[NodeId; 2]>::default(); - for pred in graph.predecessors(node_id) { - match pred.dependent.into() { - Node::Result { value, .. } => { - let is_live_after = liveness.is_live_after(&value, pp); - if is_live_after { - live_results.push(pred.dependent); - } - } - _ => continue, - } - } - - let has_live_results = !live_results.is_empty(); - for result_node in live_results.into_iter() { - graph.add_dependency(terminator_id, result_node); - } - - // Instructions with side effects but no live results require a control dependency - if opcode.has_side_effects() && !has_live_results { - graph.add_dependency(terminator_id, node_id); - continue; - } - } -} - -fn dce( - graph: &mut DependencyGraph, - block_id: hir::Block, - function: &hir::Function, - liveness: &LivenessAnalysis, -) { - // Perform dead-code elimination - // - // Find all instruction nodes in the graph, and if none of the instruction results - // are used, or are live beyond it's containing block; and the instruction has no - // side-effects, then remove all of the nodes related to that instruction, continuing - // until there are no more nodes to process. - let mut worklist = VecDeque::<(hir::Inst, NodeId)>::from_iter( - function - .dfg - .block_insts(block_id) - .enumerate() - .map(|(i, inst)| { - ( - inst, - Node::Inst { - id: inst, - pos: i as u16, - } - .into(), - ) - }), - ); - let mut remove_nodes = Vec::::default(); - while let Some((inst, inst_node)) = worklist.pop_front() { - // If the instruction is not dead at this point, leave it alone - if !is_dead_instruction(inst, block_id, function, liveness, graph) { - continue; - } - let inst_block = function.dfg.insts[inst].block; - let inst_args = function.dfg.inst_args(inst); - let branch_info = function.dfg.analyze_branch(inst); - // Visit the immediate successors of the instruction node in the dependency graph, - // these by construction may only be Argument or BlockArgument nodes. - for succ in graph.successors(inst_node) { - let dependency_node_id = succ.dependency; - // For each argument, remove the edge from instruction to argument, and from - // argument to the item it references. If the argument references an instruction - // result in the same block, add that instruction back to the worklist to check - // again in case we have made it dead - match succ.dependency.into() { - Node::Argument(ArgumentNode::Direct { index, .. }) => { - let value = inst_args[index as usize]; - match function.dfg.value_data(value) { - hir::ValueData::Inst { - inst: value_inst, .. - } => { - let value_inst = *value_inst; - let value_inst_block = function.dfg.insts[value_inst].block; - if value_inst_block == inst_block { - let pos = function - .dfg - .block_insts(inst_block) - .position(|id| id == value_inst) - .unwrap(); - // Check `value_inst` later to see if it has been made dead - worklist.push_back(( - value_inst, - Node::Inst { - id: value_inst, - pos: pos as u16, - } - .into(), - )); - } - } - hir::ValueData::Param { .. } => {} - } - } - Node::Argument( - ArgumentNode::Indirect { - successor, index, .. - } - | ArgumentNode::Conditional { - successor, index, .. - }, - ) => { - let successor = successor as usize; - let index = index as usize; - let value = match &branch_info { - BranchInfo::SingleDest(_, args) => { - assert_eq!(successor, 0); - args[index] - } - BranchInfo::MultiDest(ref jts) => jts[successor].args[index], - BranchInfo::NotABranch => unreachable!("indirect/conditional arguments are only valid as successors of a branch instruction"), - }; - match function.dfg.value_data(value) { - hir::ValueData::Inst { - inst: value_inst, .. - } => { - let value_inst = *value_inst; - let value_inst_block = function.dfg.insts[value_inst].block; - if value_inst_block == inst_block { - let pos = function - .dfg - .block_insts(inst_block) - .position(|id| id == value_inst) - .unwrap(); - // Check `value_inst` later to see if it has been made dead - worklist.push_back(( - value_inst, - Node::Inst { - id: value_inst, - pos: pos as u16, - } - .into(), - )); - } - } - hir::ValueData::Param { .. } => {} - } - } - // This is a control dependency added intentionally, skip it - Node::Inst { .. } => continue, - // No other node types are possible - Node::Result { .. } | Node::Stack(_) => { - unreachable!("invalid successor for instruction node") - } - } - remove_nodes.push(dependency_node_id); - } - - // Remove all of the result nodes because the instruction is going away - for pred in graph.predecessors(inst_node) { - remove_nodes.push(pred.dependent); - } - - // Remove the instruction last - remove_nodes.push(inst_node); - - // All of the nodes to be removed are queued, so remove them now before we proceed - for remove_id in remove_nodes.iter().copied() { - graph.remove_node(remove_id); - } - } -} - -fn is_dead_instruction( - inst: hir::Inst, - block_id: hir::Block, - function: &hir::Function, - liveness: &LivenessAnalysis, - graph: &DependencyGraph, -) -> bool { - let results = function.dfg.inst_results(inst); - let has_side_effects = function.dfg.inst(inst).has_side_effects(); - if results.is_empty() && !has_side_effects { - return true; - } - - let pp = ProgramPoint::Block(block_id); - let is_live = results - .iter() - .copied() - .enumerate() - .any(|(result_idx, result)| { - let result_node = Node::Result { - value: result, - index: result_idx as u8, - }; - if graph.num_predecessors(result_node) > 0 { - return true; - } - liveness.is_live_after(&result, pp) - }); - - !is_live && !has_side_effects -} diff --git a/codegen/masm/src/codegen/stack.rs b/codegen/masm/src/codegen/stack.rs deleted file mode 100644 index 1f69e5e4b..000000000 --- a/codegen/masm/src/codegen/stack.rs +++ /dev/null @@ -1,1329 +0,0 @@ -use core::{ - convert::AsRef, - fmt, - hash::{Hash, Hasher}, - ops::{Index, IndexMut}, -}; - -use smallvec::{smallvec, SmallVec}; - -use miden_hir::{Felt, FieldElement, Immediate, Type, Value}; - -/// This represents a constraint an operand's usage at -/// a given program point, namely when used as an instruction -/// or block argument. -#[derive(Debug, Copy, Clone)] -pub enum Constraint { - /// The operand should be moved, consuming it - /// from the stack and making it unavailable for - /// further use. - Move, - /// The operand should be copied, preserving the - /// original value for later use. - Copy, -} - -/// A [TypedValue] is a pair of an SSA value with its known type -#[derive(Debug, Clone)] -pub struct TypedValue { - pub value: Value, - pub ty: Type, -} -impl Eq for TypedValue {} -impl PartialEq for TypedValue { - fn eq(&self, other: &Self) -> bool { - self.value == other.value - } -} -impl Ord for TypedValue { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.value.cmp(&other.value) - } -} -impl PartialOrd for TypedValue { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.value.cmp(&other.value)) - } -} -impl Hash for TypedValue { - fn hash(&self, state: &mut H) { - self.value.hash(state); - } -} -impl AsRef for TypedValue { - #[inline(always)] - fn as_ref(&self) -> &Value { - &self.value - } -} -impl fmt::Display for TypedValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {}", &self.value, &self.ty) - } -} - -/// A [ConstantValue] is either an immediate value, or a large immediate that has been -/// split into raw bytes. -#[derive(Clone)] -pub enum ConstantValue { - Imm(Immediate), - Bytes(SmallVec<[u8; 16]>), -} -impl ConstantValue { - #[inline] - pub fn ty(&self) -> Type { - match self { - Self::Imm(imm) => imm.ty(), - Self::Bytes(ref bytes) => Type::Array(Box::new(Type::U8), bytes.len()), - } - } -} -impl Eq for ConstantValue {} -impl PartialEq for ConstantValue { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Imm(ref a), Self::Imm(ref b)) => a.cmp(b).is_eq(), - (Self::Bytes(ref a), Self::Bytes(ref b)) => a == b, - (_, _) => false, - } - } -} -impl PartialEq for ConstantValue { - fn eq(&self, other: &Immediate) -> bool { - match self { - Self::Imm(ref a) => a.cmp(other).is_eq(), - _ => false, - } - } -} -impl From for ConstantValue { - fn from(imm: Immediate) -> Self { - Self::Imm(imm) - } -} -impl From<&[u8]> for ConstantValue { - fn from(bytes: &[u8]) -> Self { - Self::Bytes(SmallVec::from(bytes)) - } -} -impl fmt::Debug for ConstantValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Imm(ref imm) => fmt::Debug::fmt(imm, f), - Self::Bytes(ref bytes) => { - if !bytes.is_empty() { - write!(f, "Bytes(0x")?; - for b in bytes.iter().rev() { - write!(f, "{:02x}", b)?; - } - write!(f, ")")?; - } - Ok(()) - } - } - } -} - -/// Represents the type of operand represented on the operand stack -#[derive(Clone)] -pub enum OperandType { - /// The operand is a literal, unassociated with any value in the IR - Const(ConstantValue), - /// The operand is an SSA value of known type - Value(TypedValue), - /// The operand is an intermediate runtime value of a known type, but - /// unassociated with any value in the IR - Type(Type), -} -impl OperandType { - /// Get the type representation of this operand - pub fn ty(&self) -> Type { - match self { - Self::Const(imm) => imm.ty(), - Self::Value(TypedValue { ref ty, .. }) => ty.clone(), - Self::Type(ref ty) => ty.clone(), - } - } -} -impl fmt::Debug for OperandType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Const(value) => write!(f, "Const({value:?})"), - Self::Value(value) => write!(f, "Value({value})"), - Self::Type(ty) => write!(f, "Type({ty})"), - } - } -} -impl Eq for OperandType {} -impl PartialEq for OperandType { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Value(a), Self::Value(b)) => a == b, - (Self::Value(_), _) | (_, Self::Value(_)) => false, - (Self::Const(ref a), Self::Const(ref b)) => a == b, - (Self::Const(_), _) | (_, Self::Const(_)) => false, - (Self::Type(ref a), Self::Type(ref b)) => a == b, - } - } -} -impl PartialEq for OperandType { - fn eq(&self, other: &Type) -> bool { - match self { - Self::Type(a) => a == other, - _ => false, - } - } -} -impl PartialEq for OperandType { - fn eq(&self, other: &Immediate) -> bool { - match self { - Self::Const(a) => a == other, - _ => false, - } - } -} -impl PartialEq for OperandType { - fn eq(&self, other: &Value) -> bool { - match self { - Self::Value(this) => &this.value == other, - _ => false, - } - } -} -impl From for OperandType { - fn from(value: TypedValue) -> Self { - Self::Value(value) - } -} -impl From for OperandType { - fn from(ty: Type) -> Self { - Self::Type(ty) - } -} -impl From for OperandType { - fn from(value: bool) -> Self { - Self::Const(Immediate::I1(value).into()) - } -} -impl From for OperandType { - fn from(value: u8) -> Self { - Self::Const(Immediate::U8(value).into()) - } -} -impl From for OperandType { - fn from(value: u16) -> Self { - Self::Const(Immediate::U16(value).into()) - } -} -impl From for OperandType { - fn from(value: u32) -> Self { - Self::Const(Immediate::U32(value).into()) - } -} -impl From for OperandType { - fn from(value: u64) -> Self { - Self::Const(Immediate::U64(value).into()) - } -} -impl From for OperandType { - fn from(value: Felt) -> Self { - Self::Const(Immediate::Felt(value).into()) - } -} -impl From for OperandType { - fn from(value: Immediate) -> Self { - Self::Const(value.into()) - } -} -impl From<&[u8]> for OperandType { - fn from(value: &[u8]) -> Self { - Self::Const(value.into()) - } -} - -/// This type represents a logical operand on the stack, which may consist -/// of one or more "parts", up to a word in size, on the actual stack. -/// -/// The [OperandStack] operates in terms of [Operand], but when emitting -/// Miden Assembly, we must know how to translate operand-oriented operations -/// into equivalent element-/word-oriented operations. This is accomplished -/// by tracking the low-level representation of a given operand in this struct. -#[derive(Debug, Clone)] -pub struct Operand { - /// The section of stack corresponding to this operand, containing - /// up to a full word of elements. No chunk will ever exceed a word - /// in size. This field behaves like a miniature [OperandStack], i.e. - /// elements are pushed and popped off the end to modify it. - /// - /// An operand is encoded on this stack in order of lowest - /// addressed bytes first. For example, given a struct operand, - /// the first field of the struct will be closest to the top of - /// the stack. - word: SmallVec<[Type; 4]>, - /// The high-level operand represented by this item. - /// - /// If the operand stack is manipulated in such a way that the operand - /// is torn apart, say one field of a struct is popped; then this will - /// be set to a `Type` operand, representing what high-level information - /// we have about the remaining parts of the original operand on the stack. - operand: OperandType, -} -impl Default for Operand { - fn default() -> Self { - Self { - word: smallvec![Type::Felt], - operand: Felt::ZERO.into(), - } - } -} -impl PartialEq for Operand { - #[inline(always)] - fn eq(&self, other: &Value) -> bool { - self.operand.eq(other) - } -} -impl PartialEq for Operand { - #[inline(always)] - fn eq(&self, other: &Immediate) -> bool { - self.operand.eq(other) - } -} -impl PartialEq for &Operand { - #[inline(always)] - fn eq(&self, other: &Immediate) -> bool { - self.operand.eq(other) - } -} -impl PartialEq for Operand { - #[inline(always)] - fn eq(&self, other: &Type) -> bool { - self.operand.eq(other) - } -} -impl PartialEq for &Operand { - #[inline(always)] - fn eq(&self, other: &Type) -> bool { - self.operand.eq(other) - } -} -impl From for Operand { - #[inline] - fn from(imm: Immediate) -> Self { - Self::new(imm.into()) - } -} -impl From for Operand { - #[inline] - fn from(imm: u32) -> Self { - Self::new(Immediate::U32(imm).into()) - } -} -impl TryFrom<&Operand> for Value { - type Error = (); - - fn try_from(operand: &Operand) -> Result { - match operand.operand { - OperandType::Value(TypedValue { value, .. }) => Ok(value), - _ => Err(()), - } - } -} -#[cfg(test)] -impl TryFrom<&Operand> for Immediate { - type Error = (); - - fn try_from(operand: &Operand) -> Result { - match operand.operand { - OperandType::Const(ConstantValue::Imm(ref imm)) => Ok(*imm), - _ => Err(()), - } - } -} -#[cfg(test)] -impl TryFrom<&Operand> for Type { - type Error = (); - - fn try_from(operand: &Operand) -> Result { - match operand.operand { - OperandType::Type(ref ty) => Ok(ty.clone()), - _ => Err(()), - } - } -} -#[cfg(test)] -impl TryFrom for Type { - type Error = (); - - fn try_from(operand: Operand) -> Result { - match operand.operand { - OperandType::Type(ty) => Ok(ty), - _ => Err(()), - } - } -} -impl From for Operand { - #[inline] - fn from(ty: Type) -> Self { - Self::new(OperandType::Type(ty)) - } -} -impl From for Operand { - #[inline] - fn from(value: TypedValue) -> Self { - Self::new(OperandType::Value(value)) - } -} -impl Operand { - pub fn new(operand: OperandType) -> Self { - let ty = operand.ty(); - let mut word = ty.to_raw_parts().expect("invalid operand type"); - assert!(!word.is_empty(), "invalid operand: must be a sized type"); - assert!( - word.len() <= 4, - "invalid operand: must be smaller than or equal to a word" - ); - if word.len() > 1 { - word.reverse(); - } - Self { word, operand } - } - - /// Get the size of this operand in field elements - pub fn size(&self) -> usize { - self.word.len() - } - - /// Get the [OperandType] representing the value of this operand - #[inline(always)] - pub fn value(&self) -> &OperandType { - &self.operand - } - - /// Get this operand as a [Value] - #[inline] - pub fn as_value(&self) -> Option { - self.try_into().ok() - } - - /// Get the [Type] of this operand - #[inline] - pub fn ty(&self) -> Type { - self.operand.ty() - } - - /// Pop a single field element from the underlying stack segment corresponding to this operand, - /// as a new [Operand]. - /// - /// For operands that fit in a field element, this is equivalent to cloning the operand. - /// For operands that are larger than a field element, the type will be split at the fourth byte, - /// this may destroy the semantics of a higher-level type (i.e. a large integer might become a - /// smaller one, or an array of raw bytes). It is assumed that the caller is doing this intentionally. - #[allow(unused)] - pub fn pop(&mut self) -> Operand { - if self.word.len() == 1 { - self.clone() - } else { - match &mut self.operand { - OperandType::Const(ref mut imm) => match imm { - ConstantValue::Bytes(ref mut bytes) => { - assert!(bytes.len() > 4); - let taken = ConstantValue::Bytes(SmallVec::from(&bytes[0..4])); - let new_bytes = SmallVec::from(&bytes[4..]); - *bytes = new_bytes; - let ty = self.word.pop().unwrap(); - Self { - word: smallvec![ty], - operand: OperandType::Const(taken), - } - } - ConstantValue::Imm(Immediate::U64(i)) => { - let lo = *i & (u32::MAX as u64); - let hi = *i & !(u32::MAX as u64); - *imm = ConstantValue::Imm(Immediate::U32(lo as u32)); - let ty = self.word.pop().unwrap(); - Self { - word: smallvec![ty], - operand: Immediate::U32((hi >> 32) as u32).into(), - } - } - ConstantValue::Imm(Immediate::I64(i)) => { - let i = *i as u64; - let lo = i & (u32::MAX as u64); - let hi = i & !(u32::MAX as u64); - *imm = ConstantValue::Imm(Immediate::U32(lo as u32)); - let ty = self.word.pop().unwrap(); - Self { - word: smallvec![ty], - operand: Immediate::U32((hi >> 32) as u32).into(), - } - } - ConstantValue::Imm(Immediate::F64(f)) => { - let bytes = f.to_le_bytes(); - let hi = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); - let lo = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]); - *imm = ConstantValue::Imm(Immediate::U32(lo)); - let ty = self.word.pop().unwrap(); - Self { - word: smallvec![ty], - operand: Immediate::U32(hi).into(), - } - } - ConstantValue::Imm(Immediate::I128(i)) => { - let i = *i as u128; - let bytes = i.to_le_bytes(); - let hi = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); - *imm = ConstantValue::Bytes(SmallVec::from(&bytes[4..])); - let ty = self.word.pop().unwrap(); - Self { - word: smallvec![ty], - operand: Immediate::U32(hi).into(), - } - } - ConstantValue::Imm(_) => unreachable!(), - }, - OperandType::Value(ref tv) => match tv.ty.clone().split(4) { - (ty, Some(rest)) => { - let operand = OperandType::Type(ty); - self.operand = OperandType::Type(rest); - let ty = self.word.pop().unwrap(); - Self { - word: smallvec![ty], - operand, - } - } - (_, None) => unreachable!(), - }, - OperandType::Type(ref ty) => match ty.clone().split(4) { - (ty, Some(rest)) => { - let operand = OperandType::Type(ty); - self.operand = OperandType::Type(rest); - let ty = self.word.pop().unwrap(); - Self { - word: smallvec![ty], - operand, - } - } - (_, None) => unreachable!(), - }, - } - } - } -} - -/// This structure emulates the state of the VM's operand stack while -/// generating code from the SSA representation of a function. -/// -/// In order to emit efficient and correct stack manipulation code, we must be able to -/// reason about where values are on the operand stack at a given program point. This -/// structure tracks what SSA values have been pushed on the operand stack, where they are -/// on the stack relative to the top, and whether a given stack slot aliases multiple -/// values. -/// -/// In addition to the state tracked, this structure also has an API that mimics the -/// stack manipulation instructions we can emit in the code generator, so that as we -/// emit instructions and modify this structure at the same time, 1:1. -#[derive(Clone)] -pub struct OperandStack { - stack: Vec, -} -impl Default for OperandStack { - fn default() -> Self { - Self { - stack: Vec::with_capacity(16), - } - } -} -impl OperandStack { - /// Renames the `n`th operand from the top of the stack to `value` - /// - /// The type is assumed to remain unchanged - pub fn rename(&mut self, n: usize, value: Value) { - match &mut self[n].operand { - OperandType::Value(TypedValue { - value: ref mut prev_value, - .. - }) => { - *prev_value = value; - } - prev => { - let ty = prev.ty(); - *prev = OperandType::Value(TypedValue { value, ty }); - } - } - } - - /// Searches for the position on the stack containing the operand corresponding to `value`. - /// - /// NOTE: This function will panic if `value` is not on the stack - pub fn find(&self, value: &Value) -> Option { - self.stack.iter().rev().position(|v| v == value) - } - - /// Returns true if the operand stack is empty - #[allow(unused)] - #[inline(always)] - pub fn is_empty(&self) -> bool { - self.stack.is_empty() - } - - /// Returns the number of field elements on the stack - #[inline] - pub fn raw_len(&self) -> usize { - self.stack.iter().map(|operand| operand.size()).sum() - } - - /// Returns the index in the actual runtime stack which corresponds to - /// the first element of the operand at `index`. - #[track_caller] - pub fn effective_index(&self, index: usize) -> usize { - assert!( - index < self.stack.len(), - "expected {} to be less than {}", - index, - self.stack.len() - ); - - self.stack.iter().rev().take(index).map(|o| o.size()).sum() - } - - /// Returns the index in the actual runtime stack which corresponds to - /// the last element of the operand at `index`. - #[track_caller] - pub fn effective_index_inclusive(&self, index: usize) -> usize { - assert!(index < self.stack.len()); - - self.stack - .iter() - .rev() - .take(index + 1) - .map(|o| o.size()) - .sum::() - - 1 - } - - /// Returns the number of operands on the stack - #[inline] - pub fn len(&self) -> usize { - self.stack.len() - } - - /// Returns the operand on top of the stack, without consuming it - #[inline] - pub fn peek(&self) -> Option<&Operand> { - self.stack.last() - } - - /// Returns the word on top of the stack, without consuming it - /// - /// NOTE: A word will always be 4 field elements, so if an operand is - /// larger than a field element, it may be split into an appropriately- - /// sized operand in order to fit. - #[allow(unused)] - #[inline] - pub fn peekw(&self) -> Option<[Operand; 4]> { - use core::mem::MaybeUninit; - - let end = self.stack.len().checked_sub(1)?; - if self.raw_len() < 4 { - return None; - } - - let mut word = MaybeUninit::<[Operand; 4]>::uninit(); - let mut stack = self.stack[(end - 3)..].to_vec(); - let ptr = word.as_mut_ptr() as *mut Operand; - let mut index = 0usize; - while index < 4 { - let mut elem = stack.pop().unwrap(); - let ptr = unsafe { ptr.add(index) }; - match elem.size() { - 1 => { - unsafe { - ptr.write(elem); - } - index += 1; - } - _ => { - let a = elem.pop(); - unsafe { - ptr.write(a); - } - index += 1; - stack.push(elem); - } - } - } - - Some(unsafe { MaybeUninit::assume_init(word) }) - } - - /// Pushes a word of zeroes on top of the stack - #[allow(unused)] - pub fn padw(&mut self) { - let default = Operand { - word: smallvec![Type::U32], - operand: 0u32.into(), - }; - self.stack.push(default.clone()); - self.stack.push(default.clone()); - self.stack.push(default.clone()); - self.stack.push(default); - } - - /// Pushes an operand on top of the stack - #[inline] - pub fn push>(&mut self, value: V) { - self.stack.push(value.into()); - } - - /// Pushes a word of operands on top of the stack - /// - /// NOTE: This function will panic if any of the operands are larger than a field element. - #[inline] - #[allow(unused)] - pub fn pushw(&mut self, mut word: [Operand; 4]) { - assert!( - word.iter().all(|op| op.size() == 1), - "a word must be exactly 4 field elements in size" - ); - word.reverse(); - self.stack.extend(word); - } - - /// Pops the operand on top of the stack - #[inline] - pub fn pop(&mut self) -> Option { - self.stack.pop() - } - - /// Pops the first word on top of the stack - /// - /// NOTE: A word will always be 4 field elements, so if an operand is - /// larger than a field element, it may be split into an appropriately- - /// sized operand in order to fit. - #[allow(unused)] - pub fn popw(&mut self) -> Option<[Operand; 4]> { - use core::mem::MaybeUninit; - - if self.raw_len() < 4 { - return None; - } - - let mut word = MaybeUninit::<[Operand; 4]>::uninit(); - let ptr = word.as_mut_ptr() as *mut Operand; - let mut index = 0usize; - while index < 4 { - let mut elem = self.stack.pop().unwrap(); - let ptr = unsafe { ptr.add(index) }; - match elem.size() { - 1 => { - unsafe { - ptr.write(elem); - } - index += 1; - } - _ => { - let a = elem.pop(); - unsafe { - ptr.write(a); - } - index += 1; - self.stack.push(elem); - } - } - } - - Some(unsafe { MaybeUninit::assume_init(word) }) - } - - /// Drops the top operand on the stack - pub fn drop(&mut self) { - self.stack.pop().expect("operand stack is empty"); - } - - /// Drops the top word on the stack - /// - /// NOTE: A word will always be 4 field elements, so if an operand is - /// larger than a field element, it may be split to accomodate the request. - #[allow(unused)] - pub fn dropw(&mut self) { - assert!( - self.raw_len() >= 4, - "expected at least a word on the operand stack" - ); - let mut dropped = 0usize; - while let Some(mut elem) = self.stack.pop() { - let needed = 4 - dropped; - let size = elem.size(); - dropped += size; - match size { - n if needed == n => break, - n if needed < n => { - for _ in 0..needed { - elem.pop(); - } - self.stack.push(elem); - break; - } - _ => continue, - } - } - } - - /// Drops the top `n` operands on the stack - #[inline] - pub fn dropn(&mut self, n: usize) { - let len = self.stack.len(); - assert!( - n <= len, - "unable to drop {} operands, operand stack only has {}", - n, - len - ); - self.stack.truncate(len - n); - } - - /// Duplicates the operand in the `n`th position on the stack - /// - /// If `n` is 0, duplicates the top of the stack. - pub fn dup(&mut self, n: usize) { - let operand = self[n].clone(); - self.stack.push(operand); - } - - /// Swaps the `n`th operand from the top of the stack, with the top of the stack - /// - /// If `n` is 1, it swaps the first two operands on the stack. - /// - /// NOTE: This function will panic if `n` is 0, or out of bounds. - pub fn swap(&mut self, n: usize) { - assert_ne!(n, 0, "invalid swap, index must be in the range 1..=15"); - let len = self.stack.len(); - assert!( - n < len, - "invalid operand stack index ({}), only {} operands are available", - n, - len - ); - let a = len - 1; - let b = a - n; - self.stack.swap(a, b); - } - - /// Moves the `n`th operand to the top of the stack - /// - /// If `n` is 1, this is equivalent to `swap(1)`. - /// - /// NOTE: This function will panic if `n` is 0, or out of bounds. - pub fn movup(&mut self, n: usize) { - assert_ne!(n, 0, "invalid move, index must be in the range 1..=15"); - let len = self.stack.len(); - assert!( - n < len, - "invalid operand stack index ({}), only {} operands are available", - n, - len - ); - // Pick the midpoint by counting backwards from the end - let mid = len - (n + 1); - // Split the stack, and rotate the half that - // contains our desired value to place it on top. - let (_, r) = self.stack.split_at_mut(mid); - r.rotate_left(1); - } - - /// Makes the operand on top of the stack, the `n`th operand on the stack - /// - /// If `n` is 1, this is equivalent to `swap(1)`. - /// - /// NOTE: This function will panic if `n` is 0, or out of bounds. - pub fn movdn(&mut self, n: usize) { - assert_ne!(n, 0, "invalid move, index must be in the range 1..=15"); - let len = self.stack.len(); - assert!( - n < len, - "invalid operand stack index ({}), only {} operands are available", - n, - len - ); - // Split the stack so that the desired position is in the top half - let mid = len - (n + 1); - let (_, r) = self.stack.split_at_mut(mid); - // Move all elements above the `n`th position up by one, moving the top element to the `n`th position - r.rotate_right(1); - } - - #[allow(unused)] - #[inline(always)] - pub fn iter(&self) -> impl DoubleEndedIterator { - self.stack.iter() - } -} -impl Index for OperandStack { - type Output = Operand; - - fn index(&self, index: usize) -> &Self::Output { - let len = self.stack.len(); - assert!( - index < len, - "invalid operand stack index ({}): only {} operands are available", - index, - len - ); - let effective_len: usize = self - .stack - .iter() - .rev() - .take(index + 1) - .map(|o| o.size()) - .sum(); - assert!( - effective_len <= 16, - "invalid operand stack index ({}): requires access to more than 16 elements, which is not supported in Miden", - index - ); - &self.stack[len - index - 1] - } -} -impl IndexMut for OperandStack { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - let len = self.stack.len(); - assert!( - index < len, - "invalid operand stack index ({}): only {} elements are available", - index, - len - ); - let effective_len: usize = self - .stack - .iter() - .rev() - .take(index + 1) - .map(|o| o.size()) - .sum(); - assert!( - effective_len <= 16, - "invalid operand stack index ({}): requires access to more than 16 elements, which is not supported in Miden", - index - ); - &mut self.stack[len - index - 1] - } -} - -impl fmt::Debug for OperandStack { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - #[derive(Debug)] - #[allow(unused)] - struct StackEntry<'a> { - index: usize, - value: &'a Operand, - } - - f.debug_list() - .entries( - self.stack - .iter() - .rev() - .enumerate() - .map(|(index, value)| StackEntry { index, value }), - ) - .finish() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use miden_hir::{Immediate, StructType, Type}; - - #[test] - fn operand_stack_homogenous_operand_sizes_test() { - let mut stack = OperandStack::default(); - - let zero = Immediate::U32(0); - let one = Immediate::U32(1); - let two = Immediate::U32(2); - let three = Immediate::U32(3); - let four = Immediate::U32(4); - let five = Immediate::U32(5); - let six = Immediate::U32(6); - let seven = Immediate::U32(7); - - #[inline] - fn as_imms(word: [Operand; 4]) -> [Immediate; 4] { - [ - (&word[0]).try_into().unwrap(), - (&word[1]).try_into().unwrap(), - (&word[2]).try_into().unwrap(), - (&word[3]).try_into().unwrap(), - ] - } - - #[inline] - fn as_imm(operand: Operand) -> Immediate { - (&operand).try_into().unwrap() - } - - // push - stack.push(zero); - stack.push(one); - stack.push(two); - stack.push(three); - assert_eq!(stack.len(), 4); - assert_eq!(stack[0], three); - assert_eq!(stack[1], two); - assert_eq!(stack[2], one); - assert_eq!(stack[3], zero); - - // peek - assert_eq!(stack.peek().unwrap(), three); - - // peekw - assert_eq!(stack.peekw().map(as_imms), Some([three, two, one, zero])); - - // dup - stack.dup(0); - assert_eq!(stack.len(), 5); - assert_eq!(stack[0], three); - assert_eq!(stack[1], three); - assert_eq!(stack[2], two); - assert_eq!(stack[3], one); - assert_eq!(stack[4], zero); - - stack.dup(3); - assert_eq!(stack.len(), 6); - assert_eq!(stack[0], one); - assert_eq!(stack[1], three); - assert_eq!(stack[2], three); - assert_eq!(stack[3], two); - assert_eq!(stack[4], one); - assert_eq!(stack[5], zero); - - // drop - stack.drop(); - assert_eq!(stack.len(), 5); - assert_eq!(stack[0], three); - assert_eq!(stack[1], three); - assert_eq!(stack[2], two); - assert_eq!(stack[3], one); - assert_eq!(stack[4], zero); - - // padw - stack.padw(); - assert_eq!(stack.len(), 9); - assert_eq!(stack[0], zero); - assert_eq!(stack[1], zero); - assert_eq!(stack[2], zero); - assert_eq!(stack[3], zero); - assert_eq!(stack[4], three); - assert_eq!(stack[5], three); - - // popw - assert_eq!(stack.popw().map(as_imms), Some([zero, zero, zero, zero])); - assert_eq!(stack.len(), 5); - - // pushw - stack.pushw([four.into(), five.into(), six.into(), seven.into()]); - assert_eq!(stack.len(), 9); - assert_eq!(stack[0], four); - assert_eq!(stack[1], five); - assert_eq!(stack[2], six); - assert_eq!(stack[3], seven); - assert_eq!(stack[4], three); - assert_eq!(stack[5], three); - - // dropw - stack.dropw(); - assert_eq!(stack.len(), 5); - assert_eq!(stack[0], three); - assert_eq!(stack[1], three); - assert_eq!(stack[2], two); - assert_eq!(stack[3], one); - assert_eq!(stack[4], zero); - - // swap - stack.swap(2); - assert_eq!(stack.len(), 5); - assert_eq!(stack[0], two); - assert_eq!(stack[1], three); - assert_eq!(stack[2], three); - assert_eq!(stack[3], one); - assert_eq!(stack[4], zero); - - stack.swap(1); - assert_eq!(stack.len(), 5); - assert_eq!(stack[0], three); - assert_eq!(stack[1], two); - assert_eq!(stack[2], three); - assert_eq!(stack[3], one); - assert_eq!(stack[4], zero); - - // movup - stack.movup(2); - assert_eq!(stack.len(), 5); - assert_eq!(stack[0], three); - assert_eq!(stack[1], three); - assert_eq!(stack[2], two); - assert_eq!(stack[3], one); - assert_eq!(stack[4], zero); - - // movdn - stack.movdn(3); - assert_eq!(stack.len(), 5); - assert_eq!(stack[0], three); - assert_eq!(stack[1], two); - assert_eq!(stack[2], one); - assert_eq!(stack[3], three); - assert_eq!(stack[4], zero); - - // pop - assert_eq!(stack.pop().map(as_imm), Some(three)); - assert_eq!(stack.len(), 4); - assert_eq!(stack[0], two); - assert_eq!(stack[1], one); - assert_eq!(stack[2], three); - assert_eq!(stack[3], zero); - - // dropn - stack.dropn(2); - assert_eq!(stack.len(), 2); - assert_eq!(stack[0], three); - assert_eq!(stack[1], zero); - } - - #[test] - fn operand_stack_values_test() { - use miden_hir::Value; - let mut stack = OperandStack::default(); - - let zero = Value::from_u32(0); - let one = Value::from_u32(1); - let two = Value::from_u32(2); - let three = Value::from_u32(3); - - // push - stack.push(TypedValue { - value: zero, - ty: Type::Ptr(Box::new(Type::U8)), - }); - stack.push(TypedValue { - value: one, - ty: Type::Array(Box::new(Type::U8), 4), - }); - stack.push(TypedValue { - value: two, - ty: Type::U32, - }); - stack.push(TypedValue { - value: three, - ty: Type::Struct(StructType::new([Type::U64, Type::U8])), - }); - assert_eq!(stack.len(), 4); - assert_eq!(stack.raw_len(), 6); - - assert_eq!(stack.find(&zero), Some(3)); - assert_eq!(stack.find(&one), Some(2)); - assert_eq!(stack.find(&two), Some(1)); - assert_eq!(stack.find(&three), Some(0)); - - // dup - stack.dup(0); - assert_eq!(stack.find(&three), Some(0)); - - stack.dup(3); - assert_eq!(stack.find(&one), Some(0)); - - // drop - stack.drop(); - assert_eq!(stack.find(&one), Some(3)); - assert_eq!(stack.find(&three), Some(0)); - assert_eq!(stack[1], three); - - // padw - stack.padw(); - assert_eq!(stack.find(&one), Some(7)); - assert_eq!(stack.find(&three), Some(4)); - - // rename - let four = Value::from_u32(4); - stack.rename(1, four); - assert_eq!(stack.find(&four), Some(1)); - assert_eq!(stack.find(&three), Some(4)); - - // pop - let top = stack.pop().unwrap(); - assert_eq!((&top).try_into(), Ok(Immediate::U32(0))); - assert_eq!(stack.find(&four), Some(0)); - assert_eq!(stack[1], Immediate::U32(0)); - assert_eq!(stack[2], Immediate::U32(0)); - assert_eq!(stack.find(&three), Some(3)); - - // dropn - stack.dropn(3); - assert_eq!(stack.find(&four), None); - assert_eq!(stack.find(&three), Some(0)); - assert_eq!(stack[1], three); - assert_eq!(stack.find(&two), Some(2)); - assert_eq!(stack.find(&one), Some(3)); - assert_eq!(stack.find(&zero), Some(4)); - - // swap - stack.swap(3); - assert_eq!(stack.find(&one), Some(0)); - assert_eq!(stack.find(&three), Some(1)); - assert_eq!(stack.find(&two), Some(2)); - assert_eq!(stack[3], three); - - stack.swap(1); - assert_eq!(stack.find(&three), Some(0)); - assert_eq!(stack.find(&one), Some(1)); - assert_eq!(stack.find(&two), Some(2)); - assert_eq!(stack.find(&zero), Some(4)); - - // movup - stack.movup(2); - assert_eq!(stack.find(&two), Some(0)); - assert_eq!(stack.find(&three), Some(1)); - assert_eq!(stack.find(&one), Some(2)); - assert_eq!(stack.find(&zero), Some(4)); - - // movdn - stack.movdn(3); - assert_eq!(stack.find(&three), Some(0)); - assert_eq!(stack.find(&one), Some(1)); - assert_eq!(stack[2], three); - assert_eq!(stack.find(&two), Some(3)); - assert_eq!(stack.find(&zero), Some(4)); - } - - #[test] - fn operand_stack_heterogenous_operand_sizes_test() { - let mut stack = OperandStack::default(); - - let zero = Immediate::U32(0); - let one = Immediate::U32(1); - let two = Type::U64; - let three = Type::U64; - let struct_a = Type::Struct(StructType::new([ - Type::Ptr(Box::new(Type::U8)), - Type::U16, - Type::U32, - ])); - - // push - stack.push(zero); - stack.push(one); - stack.push(two.clone()); - stack.push(three.clone()); - stack.push(struct_a.clone()); - assert_eq!(stack.len(), 5); - assert_eq!(stack.raw_len(), 9); - assert_eq!(stack[0], struct_a); - assert_eq!(stack[1], three); - assert_eq!(stack[2], two); - assert_eq!(stack[3], one); - assert_eq!(stack[4], zero); - - // peek - assert_eq!(stack.peek().unwrap(), struct_a); - - // peekw - let word = stack.peekw().unwrap(); - let struct_parts = struct_a.clone().to_raw_parts().unwrap(); - let u64_parts = three.clone().to_raw_parts().unwrap(); - assert_eq!(struct_parts.len(), 3); - assert_eq!(u64_parts.len(), 2); - assert_eq!(&word[0], &struct_parts[0]); - assert_eq!(&word[1], &struct_parts[1]); - assert_eq!(&word[2], &struct_parts[2]); - assert_eq!(&word[3], &u64_parts[0]); - - // dup - stack.dup(0); - assert_eq!(stack.len(), 6); - assert_eq!(stack.raw_len(), 12); - assert_eq!(stack[0], struct_a); - assert_eq!(stack[1], struct_a); - assert_eq!(stack[2], three); - assert_eq!(stack[3], two); - assert_eq!(stack[4], one); - assert_eq!(stack[5], zero); - assert_eq!(stack.effective_index(3), 8); - - stack.dup(3); - assert_eq!(stack.len(), 7); - assert_eq!(stack.raw_len(), 14); - assert_eq!(stack[0], two); - assert_eq!(stack[1], struct_a); - assert_eq!(stack[2], struct_a); - - // drop - stack.drop(); - assert_eq!(stack.len(), 6); - assert_eq!(stack.raw_len(), 12); - assert_eq!(stack[0], struct_a); - assert_eq!(stack[1], struct_a); - assert_eq!(stack[2], three); - assert_eq!(stack[3], two); - assert_eq!(stack[4], one); - assert_eq!(stack[5], zero); - - // swap - stack.swap(2); - assert_eq!(stack.len(), 6); - assert_eq!(stack.raw_len(), 12); - assert_eq!(stack[0], three); - assert_eq!(stack[1], struct_a); - assert_eq!(stack[2], struct_a); - assert_eq!(stack[3], two); - assert_eq!(stack[4], one); - - stack.swap(1); - assert_eq!(stack.len(), 6); - assert_eq!(stack.raw_len(), 12); - assert_eq!(stack[0], struct_a); - assert_eq!(stack[1], three); - assert_eq!(stack[2], struct_a); - assert_eq!(stack[3], two); - assert_eq!(stack[4], one); - assert_eq!(stack[5], zero); - - // movup - stack.movup(4); - assert_eq!(stack.len(), 6); - assert_eq!(stack.raw_len(), 12); - assert_eq!(stack[0], one); - assert_eq!(stack[1], struct_a); - assert_eq!(stack[2], three); - assert_eq!(stack[3], struct_a); - assert_eq!(stack[4], two); - assert_eq!(stack[5], zero); - - // movdn - stack.movdn(3); - assert_eq!(stack.len(), 6); - assert_eq!(stack.raw_len(), 12); - assert_eq!(stack[0], struct_a); - assert_eq!(stack[1], three); - assert_eq!(stack[2], struct_a); - assert_eq!(stack[3], one); - assert_eq!(stack[4], two); - - // pop - let operand: Type = stack.pop().unwrap().try_into().unwrap(); - assert_eq!(operand, struct_a); - assert_eq!(stack.len(), 5); - assert_eq!(stack.raw_len(), 9); - assert_eq!(stack[0], three); - assert_eq!(stack[1], struct_a); - assert_eq!(stack[2], one); - assert_eq!(stack[3], two); - - // dropn - stack.dropn(2); - assert_eq!(stack.len(), 3); - assert_eq!(stack.raw_len(), 4); - assert_eq!(stack[0], one); - assert_eq!(stack[1], two); - assert_eq!(stack[2], zero); - } -} diff --git a/codegen/masm/src/convert.rs b/codegen/masm/src/convert.rs deleted file mode 100644 index f76d90283..000000000 --- a/codegen/masm/src/convert.rs +++ /dev/null @@ -1,181 +0,0 @@ -use miden_hir::{ - self as hir, - pass::{AnalysisManager, ConversionPass, ConversionResult}, - ConversionPassRegistration, PassInfo, -}; -use miden_hir_analysis as analysis; -use midenc_session::Session; - -use crate::{ - codegen::{FunctionEmitter, OperandStack, Scheduler, TypedValue}, - masm, -}; - -type ProgramGlobalVariableAnalysis = analysis::GlobalVariableAnalysis; -type ModuleGlobalVariableAnalysis = analysis::GlobalVariableAnalysis; - -/// Convert an HIR program or module to Miden Assembly -/// -/// This pass assumes the following statements are true, and may fail if any are not: -/// -/// * The IR has been validated, or is known to be valid -/// * If converting a single module, it must be self-contained -/// * If converting multiple modules, they must be linked into a [Program], in order to -/// ensure that there are no undefined symbols, and that the placement of global variables -/// in linear memory has been fixed. -/// * There are no critical edges in the control flow graph, or the [SplitCriticalEdges] -/// rewrite has been applied. -/// * The control flow graph is a tree, with the exception of loop header blocks. This -/// means that the only blocks with more than one predecessor are loop headers. See -/// the [Treeify] rewrite for more information. -/// -/// Any further optimizations or rewrites are considered optional. -#[derive(ConversionPassRegistration)] -pub struct ConvertHirToMasm(core::marker::PhantomData); -impl Default for ConvertHirToMasm { - fn default() -> Self { - Self(core::marker::PhantomData) - } -} -impl PassInfo for ConvertHirToMasm { - const FLAG: &'static str = "convert-hir-to-masm"; - const SUMMARY: &'static str = "Convert an HIR module or program to Miden Assembly"; - const DESCRIPTION: &'static str = "Convert an HIR module or program to Miden Assembly\n\n\ - See the module documentation for ConvertHirToMasm for more details"; -} - -impl ConversionPass for ConvertHirToMasm { - type From = Box; - type To = Box; - - fn convert( - &mut self, - mut program: Self::From, - analyses: &mut AnalysisManager, - session: &Session, - ) -> ConversionResult { - let mut masm_program = Box::new(masm::Program::from(program.as_ref())); - - // 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); - } - - Ok(masm_program) - } -} - -impl ConversionPass for ConvertHirToMasm { - type From = Box; - type To = Box; - - fn convert( - &mut self, - mut module: Self::From, - analyses: &mut AnalysisManager, - session: &Session, - ) -> ConversionResult { - use miden_hir::ProgramAnalysisKey; - - let mut masm_module = Box::new(masm::Module::new(module.name)); - - // Compute import information for this module - masm_module.imports = module.imports(); - - // If we don't have a program-wide global variable analysis, compute it using the module global table. - if !analyses.is_available::(&ProgramAnalysisKey) { - analyses.get_or_compute::(&module, session)?; - } - - // Removing a function via this cursor will move the cursor to - // the next function in the module. Once the end of the module - // is reached, the cursor will point to the null object, and - // `remove` will return `None`. - while let Some(function) = module.pop_front() { - let mut convert_to_masm = ConvertHirToMasm::<&hir::Function>::default(); - let masm_function = convert_to_masm.convert(&function, analyses, session)?; - masm_module.push_back(Box::new(masm_function)); - } - - Ok(masm_module) - } -} - -impl<'a> ConversionPass for ConvertHirToMasm<&'a hir::Function> { - type From = &'a hir::Function; - type To = masm::Function; - - fn convert( - &mut self, - f: Self::From, - analyses: &mut AnalysisManager, - session: &Session, - ) -> ConversionResult { - use miden_hir::ProgramAnalysisKey; - - let mut f_prime = masm::Function::new(f.id, f.signature.clone()); - - // Start at the function entry - { - let entry = f.dfg.entry_block(); - - let globals = analyses - .get::(&ProgramAnalysisKey) - .map(|result| result.layout().clone()) - .unwrap_or_else(|| { - let result = analyses.expect::( - &f.id.module, - "expected global variable analysis to be available", - ); - result.layout().clone() - }); - - let domtree = analyses.get_or_compute::(f, session)?; - let loops = analyses.get_or_compute::(f, session)?; - let liveness = analyses.get_or_compute::(f, session)?; - - let mut stack = OperandStack::default(); - for arg in f.dfg.block_args(entry).iter().rev().copied() { - let ty = f.dfg.value_type(arg).clone(); - stack.push(TypedValue { value: arg, ty }); - } - - let scheduler = Scheduler::new(f, &mut f_prime, &domtree, &loops, &liveness); - let schedule = scheduler.build(); - - let emitter = - FunctionEmitter::new(f, &mut f_prime, &domtree, &loops, &liveness, &globals); - emitter.emit(schedule, stack); - } - - Ok(f_prime) - } -} diff --git a/codegen/masm/src/emulator/breakpoints.rs b/codegen/masm/src/emulator/breakpoints.rs deleted file mode 100644 index bd8f0eb95..000000000 --- a/codegen/masm/src/emulator/breakpoints.rs +++ /dev/null @@ -1,373 +0,0 @@ -use std::collections::BTreeSet; - -use miden_hir::FunctionIdent; -use rustc_hash::{FxHashMap, FxHashSet}; - -use super::{Addr, BreakpointEvent, EmulatorEvent, Instruction, InstructionPointer}; -use crate::BlockId; - -/// A breakpoint can be used to force the emulator to suspend -/// execution when a specific event or condition is reached. -/// -/// When hit, control is handed back to the owner of the emulator -/// so that they can inspect the state, potentially make changes, -/// and then resume execution if desired. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Breakpoint { - /// Break after each cycle - All, - /// Break when the cycle count reaches N - Cycle(usize), - /// Step until control reaches the given instruction pointer value - At(InstructionPointer), - /// Break at loop instructions - /// - /// The break will start on the looping instruction itself, and when - /// execution resumes, will break either at the next nested loop, or - /// if a complete iteration is reached, one of two places depending on - /// the type of looping instruction we're in: - /// - /// * `while.true` will break at the `while.true` on each iteration - /// * `repeat.n` will break at the top of the loop body on each iteration - Loops, - /// Break when the given function is called - Called(FunctionIdent), - /// Break when the given watchpoint is hit - /// - /// This is also referred to as a watchpoint - Watch(WatchpointId), -} - -/// The unique identifier associated with an active [Watchpoint] -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WatchpointId(usize); -impl WatchpointId { - #[inline] - const fn index(self) -> usize { - self.0 - } -} - -/// A [Watchpoint] specifies a region of memory that will trigger -/// a breakpoint in the emulator when it is written to. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Watchpoint { - pub addr: u32, - pub size: u32, - mode: WatchMode, -} -impl Watchpoint { - pub const fn new(addr: u32, size: u32, mode: WatchMode) -> Self { - Self { addr, size, mode } - } - - pub fn mode(&self) -> WatchMode { - self.mode - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum WatchMode { - /// Treat the watchpoint like a breakpoint - Break, - /// Raise a warning when the watchpoint is hit, but do not break - Warn, - /// Publish an event when this watchpoint is hit, but do not break - Event, - /// The watchpoint is inactive - Disabled, -} - -/// The [BreakpointManager] is responsible for tracking what break- -/// and watchpoints have been created, activated/deactivated, and for -/// informing the emulator when a breakpoint was hit. -#[derive(Debug, Default)] -pub struct BreakpointManager { - /// True if we should break every cycle - break_every_cycle: bool, - /// True if we should break when returning from the current function - pub break_on_return: bool, - /// True if we should break at every loop instruction - break_loops: bool, - /// The set of cycle counts at which a breakpoint will be triggered - break_at_cycles: BTreeSet, - /// The set of functions we should break on when called - break_on_calls: FxHashSet, - /// The set of address ranges that will trigger a watchpoint - break_on_writes: Vec, - /// A mapping of blocks to instruction indices which will trigger a breakpoint - break_on_reached: FxHashMap>, -} -impl BreakpointManager { - /// Returns all watchpoints that are currently managed by this [BreakpointManager] - pub fn watchpoints(&self) -> impl Iterator + '_ { - self.break_on_writes.iter().copied() - } - - #[allow(unused)] - pub fn has_watchpoints(&self) -> bool { - !self.break_on_writes.is_empty() - } - - pub fn has_break_on_reached(&self) -> bool { - !self.break_on_reached.is_empty() - } - - /// Returns all breakpoints that are currently managed by this [BreakpointManager] - pub fn breakpoints(&self) -> impl Iterator { - BreakpointIter::new(self) - } - - /// Create a [Watchpoint] that monitors the specified memory region, using `mode` - /// to determine how writes to that region should be handled by the watchpoint - pub fn watch(&mut self, addr: u32, size: u32, mode: WatchMode) -> WatchpointId { - let id = WatchpointId(self.break_on_writes.len()); - self.break_on_writes.push(Watchpoint { addr, size, mode }); - id - } - - /// Set the watch mode for a [Watchpoint] using the identifier returned by [watch] - pub fn watch_mode(&mut self, id: WatchpointId, mode: WatchMode) { - self.break_on_writes[id.index()].mode = mode; - } - - /// Disables a [Watchpoint] using the identifier returned by [watch] - pub fn unwatch(&mut self, id: WatchpointId) { - self.break_on_writes[id.index()].mode = WatchMode::Disabled; - } - - /// Clears all watchpoints - pub fn unwatch_all(&mut self) { - self.break_on_writes.clear(); - } - - /// Set the given breakpoint - pub fn set(&mut self, bp: Breakpoint) { - use std::collections::hash_map::Entry; - - match bp { - Breakpoint::All => { - self.break_every_cycle = true; - } - Breakpoint::Cycle(cycle) => { - self.break_at_cycles.insert(cycle); - } - Breakpoint::At(ip) => match self.break_on_reached.entry(ip.block) { - Entry::Vacant(entry) => { - entry.insert(FxHashSet::from_iter([ip.index])); - } - Entry::Occupied(mut entry) => { - entry.get_mut().insert(ip.index); - } - }, - Breakpoint::Loops => { - self.break_loops = true; - } - Breakpoint::Called(id) => { - self.break_on_calls.insert(id); - } - Breakpoint::Watch(id) => { - self.break_on_writes[id.index()].mode = WatchMode::Break; - } - } - } - - /// Unset/disable the given breakpoint - pub fn unset(&mut self, bp: Breakpoint) { - match bp { - Breakpoint::All => { - self.break_every_cycle = false; - } - Breakpoint::Cycle(cycle) => { - self.break_at_cycles.remove(&cycle); - } - Breakpoint::At(ip) => { - if let Some(indices) = self.break_on_reached.get_mut(&ip.block) { - indices.remove(&ip.index); - } - } - Breakpoint::Loops => { - self.break_loops = false; - } - Breakpoint::Called(id) => { - self.break_on_calls.remove(&id); - } - Breakpoint::Watch(id) => { - self.unwatch(id); - } - } - } - - /// Clear all breakpoints, but leaves watchpoints in place - pub fn unset_all(&mut self) { - self.break_every_cycle = false; - self.break_at_cycles.clear(); - self.break_loops = false; - self.break_on_calls.clear(); - self.break_on_reached.clear(); - } - - /// Clear all breakpoints and watchpoints - pub fn clear(&mut self) { - self.unset_all(); - self.unwatch_all(); - } - - /// Force the emulator to break the next time we return from a function - pub fn break_on_return(&mut self, value: bool) { - self.break_on_return = value; - } - - /// Respond to emulator events, and return true if at least one breakpoint was hit - pub fn handle_event( - &mut self, - event: EmulatorEvent, - ip: Option, - ) -> Option { - use core::cmp::Ordering; - - match event { - EmulatorEvent::EnterFunction(id) => { - if self.break_on_calls.contains(&id) { - Some(BreakpointEvent::Called(id)) - } else { - None - } - } - EmulatorEvent::EnterLoop(block) if self.break_loops => { - Some(BreakpointEvent::Loop(block)) - } - EmulatorEvent::EnterLoop(block) => { - if self.should_break_at(block, 0) { - Some(BreakpointEvent::Reached(InstructionPointer::new(block))) - } else { - None - } - } - EmulatorEvent::CycleStart(cycle) => { - let mut cycle_hit = false; - self.break_at_cycles - .retain(|break_at_cycle| match cycle.cmp(break_at_cycle) { - Ordering::Equal => { - cycle_hit = true; - false - } - Ordering::Greater => false, - Ordering::Less => true, - }); - if cycle_hit { - Some(BreakpointEvent::ReachedCycle(cycle)) - } else if self.break_every_cycle { - Some(BreakpointEvent::Step) - } else { - None - } - } - EmulatorEvent::ExitFunction(_) if self.break_on_return => { - Some(BreakpointEvent::StepOut) - } - EmulatorEvent::ExitFunction(_) - | EmulatorEvent::ExitLoop(_) - | EmulatorEvent::Jump(_) => match ip { - Some(Instruction { ip, .. }) => { - let break_at_current_ip = self.should_break_at(ip.block, ip.index); - if break_at_current_ip { - Some(BreakpointEvent::Reached(ip)) - } else if self.break_every_cycle { - Some(BreakpointEvent::Step) - } else { - None - } - } - None => { - if self.break_every_cycle { - Some(BreakpointEvent::Step) - } else { - None - } - } - }, - EmulatorEvent::MemoryWrite { addr, size } => self - .matches_watchpoint(addr, size) - .copied() - .map(BreakpointEvent::Watch), - EmulatorEvent::Stopped | EmulatorEvent::Suspended => None, - EmulatorEvent::Breakpoint(bp) => Some(bp), - } - } - - pub fn should_break_at(&self, block: BlockId, index: usize) -> bool { - self.break_on_reached - .get(&block) - .map(|indices| indices.contains(&index)) - .unwrap_or(false) - } - - #[inline] - #[allow(unused)] - pub fn should_break_on_write(&self, addr: Addr, size: u32) -> bool { - self.matches_watchpoint(addr, size).is_some() - } - - fn matches_watchpoint(&self, addr: Addr, size: u32) -> Option<&Watchpoint> { - let end_addr = addr + size; - self.break_on_writes.iter().find(|wp| { - let wp_end = wp.addr + wp.size; - if let WatchMode::Break = wp.mode { - addr <= wp_end && end_addr >= wp.addr - } else { - false - } - }) - } -} - -struct BreakpointIter { - bps: Vec, -} -impl BreakpointIter { - fn new(bpm: &BreakpointManager) -> Self { - let mut iter = BreakpointIter { - bps: Vec::with_capacity(4), - }; - iter.bps.extend( - bpm.break_on_writes - .iter() - .enumerate() - .filter_map(|(i, wp)| { - if wp.mode == WatchMode::Break { - Some(Breakpoint::Watch(WatchpointId(i))) - } else { - None - } - }), - ); - iter.bps - .extend(bpm.break_at_cycles.iter().copied().map(Breakpoint::Cycle)); - for (block, indices) in bpm.break_on_reached.iter() { - if indices.is_empty() { - continue; - } - let block = *block; - for index in indices.iter().copied() { - iter.bps - .push(Breakpoint::At(InstructionPointer { block, index })) - } - } - if bpm.break_loops { - iter.bps.push(Breakpoint::Loops); - } - if bpm.break_every_cycle { - iter.bps.push(Breakpoint::All); - } - iter - } -} -impl Iterator for BreakpointIter { - type Item = Breakpoint; - - fn next(&mut self) -> Option { - self.bps.pop() - } -} -impl core::iter::FusedIterator for BreakpointIter {} diff --git a/codegen/masm/src/emulator/debug.rs b/codegen/masm/src/emulator/debug.rs deleted file mode 100644 index 5a866997f..000000000 --- a/codegen/masm/src/emulator/debug.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::fmt; - -use miden_hir::{Felt, FunctionIdent, OperandStack}; - -use super::{Addr, InstructionPointer, InstructionWithOp}; - -/// Represents basic information about a frame on the call stack -pub struct CallFrame { - pub function: FunctionIdent, - pub fp: Addr, - pub ip: Option, -} - -/// Represents the current state of the program being executed for use in debugging/troubleshooting -pub struct DebugInfo<'a> { - /// The current cycle count - pub cycle: usize, - /// The current function being executed - pub function: FunctionIdent, - /// The address at which locals for the current function begin - pub fp: Addr, - /// The current instruction pointer metadata, if one is pending - pub ip: Option, - /// The current state of the operand stack - pub stack: &'a OperandStack, -} -impl DebugInfo<'_> { - pub fn to_owned(self) -> DebugInfoWithStack { - let stack = self.stack.clone(); - DebugInfoWithStack { - cycle: self.cycle, - function: self.function, - fp: self.fp, - ip: self.ip, - stack, - } - } -} -impl<'a> fmt::Debug for DebugInfo<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use miden_hir::Stack; - - f.debug_struct("DebugInfo") - .field("cycle", &self.cycle) - .field("function", &self.function) - .field("fp", &self.fp) - .field("ip", &self.ip) - .field("stack", &self.stack.debug()) - .finish() - } -} - -/// Same as [DebugInfo], but takes a clone of the operand stack, rather than a reference -pub struct DebugInfoWithStack { - /// The current cycle count - pub cycle: usize, - /// The current function being executed - pub function: FunctionIdent, - /// The address at which locals for the current function begin - pub fp: Addr, - /// The current instruction pointer metadata, if one is pending - pub ip: Option, - /// The current state of the operand stack - pub stack: OperandStack, -} -impl fmt::Debug for DebugInfoWithStack { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use miden_hir::Stack; - - f.debug_struct("DebugInfo") - .field("cycle", &self.cycle) - .field("function", &self.function) - .field("fp", &self.fp) - .field("ip", &self.ip) - .field("stack", &self.stack.debug()) - .finish() - } -} diff --git a/codegen/masm/src/emulator/events.rs b/codegen/masm/src/emulator/events.rs deleted file mode 100644 index 720a2a1b1..000000000 --- a/codegen/masm/src/emulator/events.rs +++ /dev/null @@ -1,70 +0,0 @@ -use miden_hir::FunctionIdent; - -use super::{Addr, InstructionPointer}; -use crate::BlockId; - -/// A control-flow event that occurred as a side-effect of -/// advancing the instruction pointer. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ControlEffect { - /// No control effects occurred - None, - /// We jumped to a nested block - Enter, - /// We jumped to a parent block - Exit, - /// We jumped back to the start of a while loop - Loopback, - /// We started the `n`th iteration of a repeat block - Repeat(u8), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum BreakpointEvent { - /// Breakpoint was hit because we always break on each step - Step, - /// Breakpoint was hit because we are stepping out of the current function - StepOut, - /// Breakpoint for a specific clock cycle was reached - ReachedCycle(usize), - /// Breakpoint for a specific instruction pointer value was reached - Reached(InstructionPointer), - /// Breakpoint for a loop was hit - Loop(BlockId), - /// Breakpoint for the given function was hit - Called(FunctionIdent), - /// The given watchpoint was hit as a breakpoint - Watch(super::Watchpoint), -} - -#[derive(Debug, Copy, Clone)] -pub enum EmulatorEvent { - /// The start of a new cycle has begun - CycleStart(usize), - /// The specified function was called and the emulator is at the first instruction in its body - EnterFunction(FunctionIdent), - /// The emulator has returned from the specified function, and the emulator is at the first - /// instruction following it in the caller, or if there are no more instructions in the caller, - /// waiting to return from the caller function on the next resumption. - ExitFunction(FunctionIdent), - /// The emulator has entered a loop, whose body is the specified block. - /// - /// The emulator is at the first instruction in that block. - EnterLoop(BlockId), - /// The emulator has exited a loop, whose body is the specified block, and is at the first - /// instruction following it in the enclosing block. If there are no more instructions after - /// the loop, the emulator will return from the enclosing function on the next resumption. - ExitLoop(BlockId), - /// Control has transferred to `block` - /// - /// This event is only used when the control flow instruction was not a loop instruction - Jump(BlockId), - /// The emulator just performed a store to `addr` of `size` bytes - MemoryWrite { addr: Addr, size: u32 }, - /// The emulator has reached a breakpoint - Breakpoint(BreakpointEvent), - /// The emulator has suspended, and can be resumed at will - Suspended, - /// The emulator has reached the end of the program and has stopped executing - Stopped, -} diff --git a/codegen/masm/src/emulator/functions.rs b/codegen/masm/src/emulator/functions.rs deleted file mode 100644 index 6be46bbf0..000000000 --- a/codegen/masm/src/emulator/functions.rs +++ /dev/null @@ -1,781 +0,0 @@ -use std::{cell::RefCell, fmt, rc::Rc, sync::Arc}; - -use miden_hir::Felt; -use smallvec::{smallvec, SmallVec}; - -use super::{Addr, ControlEffect, EmulationError, Emulator, InstructionPointer}; -use crate::{BlockId, Function, Op}; - -/// The type signature for native Rust functions callable from MASM IR -pub type NativeFn = dyn FnMut(&mut Emulator, &[Felt]) -> Result<(), EmulationError>; - -/// We allow functions in the emulator to be defined in either MASM IR, or native Rust. -/// -/// Functions implemented in Rust are given a mutable reference to the emulator, so they -/// have virtually unlimited power, but are correspondingly very unsafe. With great -/// power comes great responsibility, etc. -#[derive(Clone)] -pub enum Stub { - /// This function has a definition in Miden Assembly - Asm(Arc), - /// This function has a native Rust implementation - Native(Rc>>), -} - -/// This enum represents a frame on the control stack -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ControlFrame { - /// A control frame used to revisit a single instruction - /// used to control entry into a loop, e.g. `while.true` - Loopback(InstructionPointer), - /// Control is in a normal block - Block(InstructionPointer), - /// Control was transferred into a while.true loop - While(InstructionPointer), - /// Control was transferred to a repeat loop - Repeat(RepeatState), -} -impl Default for ControlFrame { - fn default() -> Self { - Self::Block(InstructionPointer::new(BlockId::from_u32(0))) - } -} -impl ControlFrame { - pub const fn ip(&self) -> InstructionPointer { - match self { - Self::Loopback(ip) - | Self::Block(ip) - | Self::While(ip) - | Self::Repeat(RepeatState { ip, .. }) => *ip, - } - } - - /// Move the instruction pointer forward one instruction and return a copy to the caller - fn move_next(&mut self) -> InstructionPointer { - match self { - Self::Block(ref mut ip) - | Self::While(ref mut ip) - | Self::Repeat(RepeatState { ref mut ip, .. }) => { - ip.index += 1; - *ip - } - Self::Loopback(_) => panic!("cannot move a loopback control frame"), - } - } - - /// Move the instruction pointer backward one instruction and return a copy to the caller - #[allow(unused)] - fn move_prev(&mut self) -> InstructionPointer { - match self { - Self::Block(ref mut ip) - | Self::While(ref mut ip) - | Self::Repeat(RepeatState { ref mut ip, .. }) => { - let index = ip.index.saturating_sub(1); - ip.index = index; - *ip - } - Self::Loopback(_) => panic!("cannot move a loopback control frame"), - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct RepeatState { - /// The instruction pointer in the repeat block - pub ip: InstructionPointer, - /// Corresponds to `n` in `repeat.n`, i.e. the number of iterations to perform - pub n: u8, - /// The number of iterations completed so far - pub iterations: u8, -} - -#[derive(Debug)] -pub struct ControlStack { - /// The control frame for the current instruction being executed - current: ControlFrame, - /// The next instruction to be executed - pending: Option, - /// The control frame corresponding to the next instruction - pending_frame: Option, - /// Pending frames from which to fetch the next instruction - frames: SmallVec<[ControlFrame; 2]>, -} -impl ControlStack { - pub fn new(ip: InstructionPointer) -> Self { - let current = ControlFrame::Block(ip); - Self { - current, - pending: None, - pending_frame: Some(current), - frames: smallvec![], - } - } - - /// The instruction pointer corresponding to the currently executing instruction - #[inline(always)] - pub const fn ip(&self) -> InstructionPointer { - self.current.ip() - } - - /// Push a new control frame for a repeat loop on the stack, and move the instruction - /// pointer so that the pending instruction is the first instruction of the body - #[inline] - pub fn enter_repeat(&mut self, block: BlockId, n: u8) { - let ip = InstructionPointer::new(block); - let pending_frame = self - .pending_frame - .replace(ControlFrame::Repeat(RepeatState { - ip, - n, - iterations: 0, - })); - self.pending = None; - self.current = ControlFrame::Repeat(RepeatState { - ip, - n, - iterations: 0, - }); - if let Some(pending_frame) = pending_frame { - self.frames.push(pending_frame); - } - } - - /// Push a new control frame for a while loop on the stack, and move the instruction - /// pointer so that the pending instruction is the first instruction of the body - #[inline] - pub fn enter_while_loop(&mut self, block: BlockId) { - let ip = InstructionPointer::new(block); - let pending_frame = self.pending_frame.replace(ControlFrame::While(ip)); - // Make sure we preserve the pending frame for when we loopback to the - // while the final time, and skip over it - if let Some(pending_frame) = pending_frame { - self.frames.push(pending_frame); - } - // We need to revisit the `while.true` at least once, so we stage a special - // control frame that expires as soon as that instruction is visited. - self.frames.push(ControlFrame::Loopback(self.current.ip())); - self.pending = None; - self.current = ControlFrame::While(ip); - } - - /// Push a new control frame for a normal block on the stack, and move the instruction - /// pointer so that the pending instruction is the first instruction of the body - #[inline] - pub fn enter_block(&mut self, block: BlockId) { - let ip = InstructionPointer::new(block); - let pending_frame = self.pending_frame.replace(ControlFrame::Block(ip)); - self.pending = None; - self.current = ControlFrame::Block(ip); - if let Some(pending_frame) = pending_frame { - self.frames.push(pending_frame); - } - } - - /// Get the next instruction to execute without moving the instruction pointer - pub fn peek(&self) -> Option { - match self.pending { - None => self.pending_frame.map(|frame| Instruction { - continuing_from: None, - ip: frame.ip(), - effect: ControlEffect::Enter, - }), - pending @ Some(_) => pending, - } - } - - pub fn next(&mut self, function: &Function) -> Option { - if self.pending.is_none() { - let pending_frame = self.pending_frame?; - let ip = pending_frame.ip(); - let effect = if ip.index == 0 { - ControlEffect::Enter - } else { - ControlEffect::None - }; - self.pending = Some(Instruction { - continuing_from: None, - ip, - effect, - }); - } - - let pending = self.pending.take()?; - let mut pending_frame = self.pending_frame.unwrap(); - let current_frame = pending_frame; - - if is_last_instruction(current_frame, function) { - let pending_frame_and_effect = self.find_continuation_frame(current_frame, function); - match pending_frame_and_effect { - Some((pending_frame, effect)) => { - self.pending = Some(Instruction { - continuing_from: Some(current_frame), - ip: pending_frame.ip(), - effect, - }); - self.pending_frame = Some(pending_frame); - self.current = current_frame; - } - None => { - self.pending = None; - self.pending_frame = None; - self.current = current_frame; - } - } - } else { - pending_frame.move_next(); - self.pending = Some(Instruction { - continuing_from: None, - ip: pending_frame.ip(), - effect: ControlEffect::None, - }); - self.pending_frame = Some(pending_frame); - self.current = current_frame; - } - - Some(pending) - } - - fn find_continuation_frame( - &mut self, - current: ControlFrame, - function: &Function, - ) -> Option<(ControlFrame, ControlEffect)> { - match current { - ControlFrame::Loopback(_) => { - // This frame is usually preceded by a top-level block frame, but if - // the body of a function starts with a loop header, then there may not - // be any parent frames, in which case we're returning from the function - let continuation = self.frames.pop()?; - return Some((continuation, ControlEffect::Exit)); - } - ControlFrame::While(_) => { - // There will always be a frame available when a while frame is on the stack - let continuation = self.frames.pop().unwrap(); - return Some((continuation, ControlEffect::Loopback)); - } - ControlFrame::Repeat(repeat) => { - let next_iteration = repeat.iterations + 1; - if next_iteration < repeat.n { - let ip = InstructionPointer::new(repeat.ip.block); - let pending_frame = ControlFrame::Repeat(RepeatState { - ip, - iterations: next_iteration, - n: repeat.n, - }); - let effect = ControlEffect::Repeat(next_iteration); - return Some((pending_frame, effect)); - } - } - _ => (), - } - - let mut current = current; - loop { - let transfer_to = self.frames.pop(); - match current { - ControlFrame::While(_) => { - break Some((transfer_to.unwrap(), ControlEffect::Loopback)); - } - ControlFrame::Repeat(mut repeat) => { - let next_iteration = repeat.iterations + 1; - if next_iteration < repeat.n { - if let Some(transfer_to) = transfer_to { - self.frames.push(transfer_to); - } - let ip = InstructionPointer::new(repeat.ip.block); - repeat.iterations = next_iteration; - repeat.ip = ip; - - let pending_frame = ControlFrame::Repeat(repeat); - let effect = ControlEffect::Repeat(next_iteration); - break Some((pending_frame, effect)); - } - current = transfer_to?; - } - ControlFrame::Loopback(_) | ControlFrame::Block(_) => { - let pending_frame = transfer_to?; - if is_valid_instruction(pending_frame.ip(), function) { - break Some((pending_frame, ControlEffect::Exit)); - } - current = pending_frame; - } - } - } - } -} - -#[inline(always)] -fn is_last_instruction(frame: ControlFrame, function: &Function) -> bool { - match frame { - ControlFrame::Loopback(_) => true, - frame => { - let ip = frame.ip(); - ip.index >= function.block(ip.block).ops.len().saturating_sub(1) - } - } -} - -#[inline(always)] -fn is_valid_instruction(ip: InstructionPointer, function: &Function) -> bool { - ip.index < function.block(ip.block).ops.len() -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Instruction { - /// The new instruction pointer value - pub ip: InstructionPointer, - /// If set, this instruction is in a control frame which was suspended - /// and is now being resumed/continued. The given frame was the state - /// of that frame when the instruction pointer was advanced. - pub continuing_from: Option, - /// The control flow effect that occurred when advancing the instruction pointer - pub effect: ControlEffect, -} -impl Instruction { - pub fn with_op(self, function: &Function) -> Option { - self.op(function).map(|op| InstructionWithOp { - ip: self.ip, - continuing_from: self.continuing_from, - effect: self.effect, - op, - }) - } - - #[inline(always)] - pub fn op(&self, function: &Function) -> Option { - function.body.get(self.ip) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct InstructionWithOp { - /// The new instruction pointer value - pub ip: InstructionPointer, - /// If set, this instruction is in a control frame which was suspended - /// and is now being resumed/continued. The given frame was the state - /// of that frame when the instruction pointer was advanced. - pub continuing_from: Option, - /// The control flow effect that occurred when advancing the instruction pointer - pub effect: ControlEffect, - /// The op the instruction pointer points to - pub op: Op, -} - -/// This struct represents an activation record for a function on the call stack -/// -/// When a program begins executing, an activation record is created for the entry point, -/// and any calls made from the entry get their own activation record, recursively down the -/// call graph. -/// -/// The activation record contains state about the execution of that function of interest -/// to the emulator, in particular, the instruction pointer, the frame pointer for locals, -/// and the function-local control stack -pub struct Activation { - function: Arc, - fp: Addr, - control_stack: ControlStack, -} -impl fmt::Debug for Activation { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Activation") - .field("function", &self.function.name) - .field("fp", &self.fp) - .field("control_stack", &self.control_stack) - .finish() - } -} -impl Activation { - /// Create a new activation record for `function`, using `fp` as the frame pointer for this activation - pub fn new(function: Arc, fp: Addr) -> Self { - let block = function.body.id(); - let control_stack = ControlStack::new(InstructionPointer::new(block)); - Self { - function, - fp, - control_stack, - } - } - - #[cfg(test)] - pub fn at(function: Arc, fp: Addr, control_stack: ControlStack) -> Self { - Self { - function, - fp, - control_stack, - } - } - - #[inline(always)] - pub fn function(&self) -> &Function { - &self.function - } - - #[inline(always)] - pub fn fp(&self) -> Addr { - self.fp - } - - #[inline(always)] - pub fn ip(&self) -> InstructionPointer { - self.control_stack.ip() - } - - /// Advance to the next instruction, returning the current [InstructionWithOp] - /// - /// If all code in the function has been executed, None will be returned. - #[inline] - pub fn next(&mut self) -> Option { - self.move_next().and_then(|ix| ix.with_op(&self.function)) - } - - /// Advance to the next instruction, returning the current [Instruction] - /// - /// If all code in the function has been executed, None will be returned. - pub fn move_next(&mut self) -> Option { - self.control_stack.next(&self.function) - } - - /// Peek at the [Instruction] which will be returned when [move_next] is called next - #[inline(always)] - pub fn peek(&self) -> Option { - self.control_stack.peek() - } - - /// Peek at the [InstructionWithOp] corresponding to the next instruction to be returned from [move_next] - pub fn peek_with_op(&self) -> Option { - self.control_stack - .peek() - .and_then(|ix| ix.with_op(&self.function)) - } - - /// Peek at the [Op] coresponding to the next instruction to be returned from [move_next] - #[allow(unused)] - pub fn peek_op(&self) -> Option { - self.control_stack - .peek() - .and_then(|ix| ix.op(&self.function)) - } - - /// Set the instruction pointer to the first instruction of `block` - /// - /// This helper ensures the internal state of the activation record is maintained - pub(super) fn enter_block(&mut self, block: BlockId) { - self.control_stack.enter_block(block); - } - - /// Set the instruction pointer to the first instruction of `block`, which is the body of - /// a while loop. - /// - /// This ensures that when we attempt to leave the while loop, the "next" instruction will - /// be the `while.true` itself, so that its condition gets re-evaluated. This will result - /// in an infinite loop unless the value of the condition is zero, or the operand stack is - /// exhausted, causing an assertion. - pub(super) fn enter_while_loop(&mut self, block: BlockId) { - self.control_stack.enter_while_loop(block); - } - - /// Set the instruction pointer to the first instruction of `block`, which is the body of - /// a repeat loop with `n` iterations. - /// - /// We use an auxiliary structure to track repeat loops, so this works a bit differently - /// than `enter_while_loop`, but has the same effect. The state we track is used to determine - /// when we've executed `count` iterations of the loop and should exit to the next instruction - /// following the `repeat.N`. - pub(super) fn repeat_block(&mut self, block: BlockId, count: u8) { - self.control_stack.enter_repeat(block, count); - } -} - -#[cfg(test)] -mod tests { - use miden_hir::{assert_matches, Signature}; - use std::sync::Arc; - - use super::*; - use crate::{Function, Op}; - - #[test] - fn activation_record_start_of_block() { - let mut activation = Activation::new(test_function(), 0); - let body_blk = activation.function.body.id(); - assert_eq!( - activation.peek(), - Some(Instruction { - continuing_from: None, - ip: InstructionPointer { - block: body_blk, - index: 0 - }, - effect: ControlEffect::Enter, - }) - ); - assert_eq!(activation.peek_op(), Some(Op::PushU8(2))); - - // Advance the instruction pointer - assert_matches!( - activation.next(), - Some(InstructionWithOp { - op: Op::PushU8(2), - .. - }) - ); - assert_eq!( - activation.peek_with_op(), - Some(InstructionWithOp { - op: Op::PushU8(1), - continuing_from: None, - ip: InstructionPointer { - block: body_blk, - index: 1 - }, - effect: ControlEffect::None - }) - ); - } - - #[test] - fn activation_record_if_true_entry() { - let function = test_function(); - let body_blk = function.body.id(); - let control_stack = ControlStack::new(InstructionPointer { - block: body_blk, - index: 5, - }); - let mut activation = Activation::at(test_function(), 0, control_stack); - - assert_eq!( - activation.peek(), - Some(Instruction { - continuing_from: None, - ip: InstructionPointer { - block: body_blk, - index: 5 - }, - effect: ControlEffect::Enter - }) - ); - let Some(Op::If(then_blk, _)) = activation.peek_op() else { - panic!("expected if.true, got {:?}", activation.peek_with_op()) - }; - - // Enter the truthy branch of the if.true - activation.enter_block(then_blk); - - assert_eq!( - activation.peek_with_op(), - Some(InstructionWithOp { - op: Op::PushU8(1), - continuing_from: None, - ip: InstructionPointer { - block: then_blk, - index: 0 - }, - effect: ControlEffect::Enter, - }) - ); - - // Advance the instruction pointer - assert_matches!( - activation.next(), - Some(InstructionWithOp { - op: Op::PushU8(1), - effect: ControlEffect::Enter, - .. - }) - ); - assert_eq!( - activation.peek_with_op(), - Some(InstructionWithOp { - op: Op::While(BlockId::from_u32(3)), - continuing_from: None, - ip: InstructionPointer { - block: then_blk, - index: 1 - }, - effect: ControlEffect::None - }) - ); - } - - #[test] - fn activation_record_nested_control_flow_exit() { - let function = test_function(); - let body_blk = function.body.id(); - let control_stack = ControlStack::new(InstructionPointer { - block: body_blk, - index: 5, - }); - let mut activation = Activation::at(test_function(), 0, control_stack); - - let next = activation.next().unwrap(); - assert_eq!(ControlEffect::None, next.effect); - let Op::If(then_blk, _) = next.op else { - panic!("expected if.true, got {next:?}") - }; - - // Enter the truthy branch of the if.true - activation.enter_block(then_blk); - - // Step over the first instruction, to the `while.true` - assert_matches!( - activation.next(), - Some(InstructionWithOp { - op: Op::PushU8(1), - effect: ControlEffect::Enter, - .. - }) - ); - - let Some(Op::While(loop_body)) = activation.peek_op() else { - panic!("expected while.true, got {:?}", activation.peek_op()) - }; - - // Enter loop body - activation.next().unwrap(); - activation.enter_while_loop(loop_body); - - assert_eq!( - activation.peek_with_op(), - Some(InstructionWithOp { - op: Op::Dup(1), - continuing_from: None, - ip: InstructionPointer { - block: loop_body, - index: 0 - }, - effect: ControlEffect::Enter - }) - ); - - // Advance the instruction pointer to the end of the loop body - let next = activation.next().unwrap(); - assert_eq!(next.op, Op::Dup(1)); - let next = activation.next().unwrap(); - assert_eq!(next.op, Op::Dup(1)); - let next = activation.next().unwrap(); - assert_eq!(next.op, Op::Incr); - - // Ensure things are normal at the last instruction - assert_eq!( - activation.peek_with_op(), - Some(InstructionWithOp { - op: Op::U32Lt, - continuing_from: None, - ip: InstructionPointer { - block: loop_body, - index: 3 - }, - effect: ControlEffect::None - }) - ); - - // Advance the instruction pointer, obtaining the last instruction of the loop body - assert_eq!(activation.next().map(|ix| ix.op), Some(Op::U32Lt)); - - // Exit while.true - assert_eq!( - activation.peek_with_op(), - Some(InstructionWithOp { - op: Op::While(loop_body), - continuing_from: Some(ControlFrame::While(InstructionPointer { - block: loop_body, - index: 3 - })), - ip: InstructionPointer { - block: then_blk, - index: 1 - }, - effect: ControlEffect::Loopback, - }) - ); - assert_eq!( - activation.next(), - Some(InstructionWithOp { - op: Op::While(loop_body), - continuing_from: Some(ControlFrame::While(InstructionPointer { - block: loop_body, - index: 3 - })), - ip: InstructionPointer { - block: then_blk, - index: 1 - }, - effect: ControlEffect::Loopback, - }) - ); - - // Exit if.true - let callee = "test::foo".parse().unwrap(); - assert_eq!( - activation.peek_with_op(), - Some(InstructionWithOp { - op: Op::Exec(callee), - continuing_from: Some(ControlFrame::Loopback(InstructionPointer { - block: then_blk, - index: 1 - })), - ip: InstructionPointer { - block: body_blk, - index: 6, - }, - effect: ControlEffect::Exit, - }) - ); - assert_eq!( - activation.next(), - Some(InstructionWithOp { - op: Op::Exec(callee), - continuing_from: Some(ControlFrame::Loopback(InstructionPointer { - block: then_blk, - index: 1 - })), - ip: InstructionPointer { - block: body_blk, - index: 6, - }, - effect: ControlEffect::Exit, - }) - ); - - // Return from the function - assert_matches!(activation.next(), None); - } - - fn test_function() -> Arc { - let mut function = Function::new( - "test::main".parse().unwrap(), - Signature::new(vec![], vec![]), - ); - let then_blk = function.create_block(); - let else_blk = function.create_block(); - 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())); - } - { - let then_body = function.block_mut(then_blk); - then_body.push(Op::PushU8(1)); - then_body.push(Op::While(while_blk)); - } - { - let else_body = function.block_mut(else_blk); - else_body.push(Op::U32Max); - } - { - 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); - } - - Arc::new(function) - } -} diff --git a/codegen/masm/src/emulator/mod.rs b/codegen/masm/src/emulator/mod.rs deleted file mode 100644 index 4b16ffffc..000000000 --- a/codegen/masm/src/emulator/mod.rs +++ /dev/null @@ -1,1725 +0,0 @@ -mod breakpoints; -mod debug; -mod events; -mod functions; - -pub use self::breakpoints::*; -pub use self::debug::{CallFrame, DebugInfo, DebugInfoWithStack}; -pub use self::events::{BreakpointEvent, ControlEffect, EmulatorEvent}; -use self::functions::{Activation, Stub}; -pub use self::functions::{Instruction, InstructionWithOp, NativeFn}; - -use std::{cell::RefCell, cmp, rc::Rc, sync::Arc}; - -use miden_hir::{ - assert_matches, Felt, FieldElement, FunctionIdent, Ident, OperandStack, Stack, StarkField, -}; -use rustc_hash::{FxHashMap, FxHashSet}; - -use crate::{Begin, BlockId, Function, Module, Op, Program}; - -/// This type represents the various sorts of errors which can occur when -/// running the emulator on a MASM program. Some errors may result in panics, -/// but those which we can handle are represented here. -#[derive(Debug, Clone, thiserror::Error, PartialEq)] -pub enum EmulationError { - /// The given module is already loaded - #[error("unable to load module: '{0}' is already loaded")] - AlreadyLoaded(Ident), - /// The given function is already loaded - #[error("unable to load function: '{0}' is already loaded")] - DuplicateFunction(FunctionIdent), - /// The given function cannot be found - #[error("unable to invoke function: '{0}' is not defined")] - UndefinedFunction(FunctionIdent), - /// The emulator ran out of available memory - #[error("system limit: out of memory")] - OutOfMemory, - /// The emulator was terminated due to a program failing to terminate in its budgeted time - #[error("execution terminated prematurely: maximum cycle count reached")] - CycleBudgetExceeded, - /// A breakpoint was reached, so execution was suspended and can be resumed - #[error("execution suspended by breakpoint")] - BreakpointHit(BreakpointEvent), - /// An attempt was made to run the emulator without specifying an entrypoint - #[error("unable to start the emulator without an entrypoint")] - NoEntrypoint, -} - -/// The size/type of pointers in the emulator -pub type Addr = u32; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct InstructionPointer { - /// The block in which the instruction pointer is located - pub block: BlockId, - /// The index of the instruction pointed to - pub index: usize, -} -impl InstructionPointer { - pub const fn new(block: BlockId) -> Self { - Self { block, index: 0 } - } -} - -/// This enum represents the state transitions for the emulator. -/// -/// * The emulator starts in `Init` -/// * Once some code is loaded, it becomes `Loaded` -/// * Once the emulator has started executing some code, it becomes `Started` -/// * If the emulator suspends due to a breakpoint or stepping, it becomes `Suspended` -/// * Once the emulator finishes executing whatever entrypoint was invoked, it becomes `Stopped` -/// * If an error occurs between `Started` and `Stopped`, it becomes `Faulted` -/// -/// Once `Started`, it is not possible to `start` the emulator again until it reaches the -/// `Stopped` state, or is explicitly reset to the `Init` or `Loaded` states using `reset` -/// or `stop` respectively. -#[derive(Debug, Default)] -enum Status { - /// The emulator is in its initial state - /// - /// In this state, the emulator cannot execute any code because there - /// is no code loaded yet. - #[default] - Init, - /// A program has been loaded into the emulator, but not yet started - /// - /// This is the clean initial state from which a program or function can - /// start executing. Once the emulator leaves this status, the state of - /// the emulator is "dirty", i.e. it is no longer a clean slate. - Loaded, - /// The emulator has started running the current program, or a specified function. - Started, - /// The emulator is suspended, and awaiting resumption - Suspended, - /// The emulator finished running the current program, or a specified function, - /// and the state of the emulator has not yet been reset. - Stopped, - /// The emulator has stopped due to an error, and cannot proceed further - Faulted(EmulationError), -} - -/// [Emulator] provides us with a means to execute our MASM IR directly -/// without having to emit "real" MASM and run it via the Miden VM. -/// In other words, it's a convenient way to run tests to verify the -/// expected behavior of a program without all of the baggage of the -/// Miden VM. -/// -/// [Emulator] is necessarily a more limited execution environment: -/// -/// * It only handles instructions which are defined in the [Op] enum -/// * Anything related to proving, calling contracts, etc. is not supported -/// * The default environment is empty, i.e. there are no Miden VM standard -/// library functions available. Users must emit Miden IR for all functions -/// they wish to call, or alternatively, provide native stubs. -pub struct Emulator { - status: Status, - functions: FxHashMap, - locals: FxHashMap, - modules_loaded: FxHashMap>, - modules_pending: FxHashSet, - memory: Vec<[Felt; 4]>, - stack: OperandStack, - callstack: Vec, - hp_start: u32, - hp: u32, - lp_start: u32, - lp: u32, - breakpoints: BreakpointManager, - step_over: Option, - clk: usize, - clk_limit: usize, -} -impl Default for Emulator { - fn default() -> Self { - Self::new( - Self::DEFAULT_HEAP_SIZE, - Self::DEFAULT_HEAP_START, - Self::DEFAULT_LOCALS_START, - ) - } -} -impl Emulator { - const PAGE_SIZE: u32 = 64 * 1024; - pub const DEFAULT_HEAP_SIZE: u32 = (4 * Self::PAGE_SIZE) / 16; - pub const DEFAULT_HEAP_START: u32 = (2 * Self::PAGE_SIZE) / 16; - pub const DEFAULT_LOCALS_START: u32 = (3 * Self::PAGE_SIZE) / 16; - const EMPTY_WORD: [Felt; 4] = [Felt::ZERO; 4]; - - /// Construct a new, empty emulator with: - /// - /// * A linear memory heap of `memory_size` words - /// * The start of the usable heap set to `hp` (an address in words) - /// * The start of the reserved heap used for locals set to `lp` (an address in words) - /// - pub fn new(memory_size: u32, hp: u32, lp: u32) -> Self { - let memory = vec![Self::EMPTY_WORD; memory_size as usize]; - Self { - status: Status::Init, - functions: Default::default(), - locals: Default::default(), - modules_loaded: Default::default(), - modules_pending: Default::default(), - memory, - stack: Default::default(), - callstack: vec![], - hp_start: hp, - hp, - lp_start: lp, - lp, - breakpoints: Default::default(), - step_over: None, - clk: 0, - clk_limit: usize::MAX, - } - } - - /// Place a cap on the number of cycles the emulator will execute before failing with an error - pub fn set_max_cycles(&mut self, max: usize) { - self.clk_limit = max; - } - - /// Returns all watchpoints that are currently managed by this [BreakpointManager] - pub fn watchpoints(&self) -> impl Iterator + '_ { - self.breakpoints.watchpoints() - } - - /// Returns all breakpoints that are currently managed by this [BreakpointManager] - pub fn breakpoints(&self) -> impl Iterator { - self.breakpoints.breakpoints() - } - - /// Sets a breakpoint for the emulator - pub fn set_breakpoint(&mut self, bp: Breakpoint) { - self.breakpoints.set(bp); - } - - /// Removes the given breakpoint from the emulator - pub fn clear_breakpoint(&mut self, bp: Breakpoint) { - self.breakpoints.unset(bp); - } - - /// Removes the all breakpoints from the emulator - pub fn clear_breakpoints(&mut self) { - self.breakpoints.unset_all(); - } - - /// Sets a watchpoint in the emulator - pub fn set_watchpoint(&mut self, addr: Addr, size: u32, mode: WatchMode) -> WatchpointId { - self.breakpoints.watch(addr, size, mode) - } - - /// Sets a watchpoint in the emulator - pub fn clear_watchpoint(&mut self, id: WatchpointId) { - self.breakpoints.unwatch(id); - } - - /// Set the watch mode for a [Watchpoint] using the identifier returned by [watch] - pub fn watchpoint_mode(&mut self, id: WatchpointId, mode: WatchMode) { - self.breakpoints.watch_mode(id, mode); - } - - /// Clears all watchpoints - pub fn clear_watchpoints(&mut self) { - self.breakpoints.unwatch_all(); - } - - /// Clear all breakpoints and watchpoints - pub fn clear_break_and_watchpoints(&mut self) { - self.breakpoints.clear(); - } - - /// Get's debug information about the current emulator state - pub fn info(&self) -> Option> { - let current = self.callstack.last()?; - // This returns the pending activation state for the current function, - // i.e. the next instruction to be executed, what control flow effects - // will occur to reach that instruction, and the actual instruction pointer - let ip = current.peek_with_op(); - Some(DebugInfo { - cycle: self.clk, - function: current.function().name, - fp: current.fp(), - ip, - stack: &self.stack, - }) - } - - /// Get a stacktrace for the code running in the emulator - pub fn stacktrace(&self) -> Vec { - let mut frames = Vec::with_capacity(self.callstack.len()); - for frame in self.callstack.iter() { - frames.push(CallFrame { - function: frame.function().name, - fp: frame.fp(), - ip: Some(frame.ip()), - }) - } - frames - } - - /// Get the instruction pointer that will be next executed by the emulator - pub fn current_ip(&self) -> Option { - self.callstack - .last() - .and_then(|activation| activation.peek()) - } - - /// Get the name of the function that is currently executing - pub fn current_function(&self) -> Option { - self.callstack - .last() - .map(|activation| activation.function().name) - } - - /// Get access to the current state of the operand stack - pub fn stack(&mut self) -> &OperandStack { - &self.stack - } - - /// Get mutable access to the current state of the operand stack - pub fn stack_mut(&mut self) -> &mut OperandStack { - &mut self.stack - } - - /// Load `program` into this emulator - /// - /// This resets the emulator state, as only one program may be loaded at a time. - pub fn load_program(&mut self, program: Arc) -> Result<(), EmulationError> { - // Ensure the emulator state is reset - if !matches!(self.status, Status::Init) { - self.reset(); - } - - let modules = program.unwrap_frozen_modules(); - let mut cursor = modules.front(); - while let Some(module) = cursor.clone_pointer() { - self.load_module(module)?; - cursor.move_next(); - } - - // TODO: Load data segments - - if let Some(begin) = program.body.as_ref() { - self.load_init(begin)?; - } - - self.status = Status::Loaded; - - Ok(()) - } - - /// Load `module` into this emulator - /// - /// An error is returned if a module with the same name is already loaded. - pub fn load_module(&mut self, module: Arc) -> Result<(), EmulationError> { - use std::collections::hash_map::Entry; - - assert_matches!(self.status, Status::Init | Status::Loaded, "cannot load modules once execution has started without calling stop() or reset() first"); - - match self.modules_loaded.entry(module.name) { - Entry::Occupied(_) => return Err(EmulationError::AlreadyLoaded(module.name)), - Entry::Vacant(entry) => { - entry.insert(module.clone()); - } - } - - // Register module dependencies - for import in module.imports.iter() { - let name = Ident::with_empty_span(import.name); - if self.modules_loaded.contains_key(&name) { - continue; - } - self.modules_pending.insert(name); - } - self.modules_pending.remove(&module.name); - - // Load functions from this module - let functions = module.unwrap_frozen_functions(); - let mut cursor = functions.front(); - while let Some(function) = cursor.clone_pointer() { - self.load_function(function)?; - cursor.move_next(); - } - - self.status = Status::Loaded; - - Ok(()) - } - - /// Reloads a loaded module, `name`. - /// - /// This function will panic if the named module is not currently loaded. - pub fn reload_module(&mut self, module: Arc) -> Result<(), EmulationError> { - self.unload_module(module.name); - self.load_module(module) - } - - /// Unloads a loaded module, `name`. - /// - /// This function will panic if the named module is not currently loaded. - pub fn unload_module(&mut self, name: Ident) { - assert_matches!(self.status, Status::Loaded, "cannot unload modules once execution has started without calling stop() or reset() first"); - - let prev = self - .modules_loaded - .remove(&name) - .expect("cannot reload a module that was not previously loaded"); - - // Unload all functions associated with the previous load - for f in prev.functions() { - self.functions.remove(&f.name); - self.locals.remove(&f.name); - } - - // Determine if we need to add `name` to `modules_pending` if there are dependents still loaded - for module in self.modules_loaded.values() { - if module.imports.is_import(&name) { - self.modules_pending.insert(name); - break; - } - } - } - - /// Load the `begin` block which constitutes the initialization region for a [Program] - fn load_init(&mut self, init: &Begin) -> Result<(), EmulationError> { - use miden_hir::{attributes, Signature}; - - let main_fn = FunctionIdent { - module: miden_assembly::LibraryPath::EXEC_PATH.into(), - function: miden_assembly::ProcedureName::MAIN_PROC_NAME.into(), - }; - let mut main = Function::new(main_fn, Signature::new([], [])); - main.attrs.set(attributes::ENTRYPOINT); - main.body = init.body.clone(); - - self.load_function(Arc::new(main))?; - - Ok(()) - } - - /// Load `function` into this emulator - fn load_function(&mut self, function: Arc) -> Result<(), EmulationError> { - let id = function.name; - if self.functions.contains_key(&id) { - return Err(EmulationError::DuplicateFunction(id)); - } - let fp = self.lp; - self.lp += function.locals().len() as u32; - self.functions.insert(id, Stub::Asm(function)); - self.locals.insert(id, fp); - - Ok(()) - } - - /// Load `function` into this emulator, with the given identifier - /// - /// Because we don't know the set of [FuncId] that have already been allocated, - /// we leave the the choice up to the caller. We assert that functions do - /// not get defined twice to catch conflicts, just in case. - pub fn load_nif( - &mut self, - id: FunctionIdent, - function: Box, - ) -> Result<(), EmulationError> { - assert_matches!( - self.status, - Status::Init | Status::Loaded, - "cannot load nifs once execution has started without calling stop() or reset() first" - ); - - if self.functions.contains_key(&id) { - return Err(EmulationError::DuplicateFunction(id)); - } - self.functions - .insert(id, Stub::Native(Rc::new(RefCell::new(function)))); - - Ok(()) - } - - /// Allocate space for `value` on the emulator heap, and copy it's contents there. - /// - /// NOTE: The smallest unit of addressable memory is 4 bytes (32 bits). If you provide - /// a value that is smaller than this, or is not a multiple of 4, the data will be padded - /// with zeroes to ensure that it is. - pub fn write_bytes_to_memory(&mut self, value: &[u8]) -> u32 { - let addr = self.hp; - if value.is_empty() { - return addr; - } - - let mut elem_idx = 0; - for chunk in value.chunks(4) { - let elem = match chunk.len() { - 4 => u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]), - 3 => u32::from_le_bytes([chunk[0], chunk[1], chunk[2], 0]), - 2 => u32::from_le_bytes([chunk[0], chunk[1], 0, 0]), - 1 => u32::from_le_bytes([chunk[0], 0, 0, 0]), - 0 => 0, - _ => unreachable!(), - }; - if elem_idx == 4 { - elem_idx = 0; - assert!( - self.hp + 1 < self.lp, - "heap has overflowed into reserved region" - ); - self.hp += 1; - } - self.memory[self.hp as usize][elem_idx] = Felt::new(elem as u64); - elem_idx += 1; - } - - addr - } - - /// Allocate enough words to hold `size` bytes of memory - /// - /// Returns the pointer as a byte-addressable address - pub fn malloc(&mut self, size: usize) -> u32 { - let addr = self.hp; - - if size == 0 { - return addr; - } - - let size = size as u32; - let extra = size % 16; - let words = (size / 16) + (extra > 0) as u32; - assert!( - self.hp + words < self.lp, - "heap has overflowed into reserved region" - ); - self.hp += words; - - addr * 16 - } - - /// Write `value` to the word at `addr`, and element `index` - pub fn store(&mut self, addr: usize, value: Felt) { - use crate::NativePtr; - - let ptr = NativePtr::from_ptr(addr.try_into().expect("invalid address")); - let addr = ptr.waddr as usize; - assert_eq!(ptr.offset, 0, "invalid store: unaligned address {addr:#?}"); - assert!(addr < self.memory.len(), "invalid address"); - - self.memory[addr][ptr.index as usize] = value; - } - - /// Start executing the current program by `invoke`ing the top-level initialization block (the entrypoint). - /// - /// This function will run the program to completion, and return the state of the operand stack on exit. - /// - /// NOTE: If no entrypoint has been loaded, an error is returned. - /// - /// The emulator is automatically reset when it exits successfully. - pub fn start(&mut self) -> Result, EmulationError> { - match self.status { - Status::Init => return Err(EmulationError::NoEntrypoint), - Status::Loaded => (), - Status::Stopped => { - self.stop(); - } - Status::Started | Status::Suspended => panic!("cannot start the emulator when it is already started without calling stop() or reset() first"), - Status::Faulted(ref err) => return Err(err.clone()), - } - - let main_fn = FunctionIdent { - module: miden_assembly::LibraryPath::EXEC_PATH.into(), - function: miden_assembly::ProcedureName::MAIN_PROC_NAME.into(), - }; - - // Run to completion - let stack = self.invoke(main_fn, &[]).map_err(|err| match err { - EmulationError::UndefinedFunction(f) if f == main_fn => EmulationError::NoEntrypoint, - err => err, - })?; - - // Reset the emulator on exit - self.stop(); - - // Return the output contained on the operand stack - Ok(stack) - } - - /// Start emulation by `enter`ing the top-level initialization block (the entrypoint). - /// - /// This should be called instead of `start` when stepping through a program rather than - /// executing it to completion in one call. - /// - /// NOTE: If no entrypoint has been loaded, an error is returned. - /// - /// It is up to the caller to reset the emulator when the program exits, unlike `start`. - pub fn init(&mut self) -> Result { - match self.status { - Status::Init => return Err(EmulationError::NoEntrypoint), - Status::Loaded => (), - Status::Stopped => { - self.stop(); - } - Status::Started | Status::Suspended => panic!("cannot start the emulator when it is already started without calling stop() or reset() first"), - Status::Faulted(ref err) => return Err(err.clone()), - } - - let main_fn = FunctionIdent { - module: miden_assembly::LibraryPath::EXEC_PATH.into(), - function: miden_assembly::ProcedureName::MAIN_PROC_NAME.into(), - }; - - // Step into the entrypoint - self.enter(main_fn, &[]).map_err(|err| match err { - EmulationError::UndefinedFunction(f) if f == main_fn => EmulationError::NoEntrypoint, - err => err, - }) - } - - /// Stop running the currently executing function, and reset the cycle counter, operand stack, - /// and linear memory. - /// - /// This function preserves loaded code, breakpoints, and other configuration items. - /// - /// If an attempt is made to run the emulator in the stopped state, a panic will occur - pub fn stop(&mut self) { - self.callstack.clear(); - self.stack.clear(); - self.memory.clear(); - self.hp = self.hp_start; - self.lp = self.lp_start; - self.step_over = None; - self.clk = 0; - self.status = Status::Loaded; - } - - /// Reset the emulator state to its initial state at creation. - /// - /// In addition to resetting the cycle counter, operand stack, and linear memory, - /// this function also unloads all code, and clears all breakpoints. Only the - /// configuration used to initialize the emulator is preserved. - /// - /// To use the emulator after calling this function, you must load a program or module again. - pub fn reset(&mut self) { - self.stop(); - self.functions.clear(); - self.locals.clear(); - self.modules_loaded.clear(); - self.modules_pending.clear(); - self.breakpoints.clear(); - self.status = Status::Init; - } - - /// Run the emulator by invoking `callee` with `args` placed on the - /// operand stack in FIFO order. - /// - /// If a fatal error occurs during emulation, `Err` is returned, - /// e.g. if `callee` has not been loaded. - /// - /// When `callee` returns, it's result will be returned wrapped in `Ok`. - /// For functions with no return value, this will be `Ok(None)`, or all - /// others it will be `Ok(Some(value))`. - pub fn invoke( - &mut self, - callee: FunctionIdent, - args: &[Felt], - ) -> Result, EmulationError> { - assert_matches!(self.status, Status::Loaded, "cannot start executing a function when the emulator is already started without calling stop() or reset() first"); - let fun = self - .functions - .get(&callee) - .cloned() - .ok_or(EmulationError::UndefinedFunction(callee))?; - self.status = Status::Started; - match fun { - Stub::Asm(ref function) => match self.invoke_function(function.clone(), args) { - done @ Ok(_) => { - self.status = Status::Stopped; - done - } - Err(err @ EmulationError::BreakpointHit(_)) => { - self.status = Status::Suspended; - Err(err) - } - Err(err) => { - self.status = Status::Faulted(err.clone()); - Err(err) - } - }, - Stub::Native(function) => { - let mut function = function.borrow_mut(); - function(self, args)?; - Ok(self.stack.clone()) - } - } - } - - /// Invoke a function defined in MASM IR, placing the given arguments on the - /// operand stack in FIFO order, and suspending immediately if any breakpoints - /// would have been triggered by the invocation. - #[inline] - fn invoke_function( - &mut self, - function: Arc, - args: &[Felt], - ) -> Result, EmulationError> { - // Place the arguments on the operand stack - //assert_eq!(args.len(), function.arity()); - for arg in args.iter().copied().rev() { - self.stack.push(arg); - } - - // Schedule `function` - let name = function.name; - let fp = self.locals[&name]; - let state = Activation::new(function, fp); - self.callstack.push(state); - - match self - .breakpoints - .handle_event(EmulatorEvent::EnterFunction(name), self.current_ip()) - { - Some(bp) => Err(EmulationError::BreakpointHit(bp)), - None => { - self.run()?; - - Ok(self.stack.clone()) - } - } - } - - /// Run the emulator by invoking `callee` with `args` placed on the - /// operand stack in FIFO order. - /// - /// If a fatal error occurs during emulation, `Err` is returned, - /// e.g. if `callee` has not been loaded. - /// - /// When `callee` returns, it's result will be returned wrapped in `Ok`. - /// For functions with no return value, this will be `Ok(None)`, or all - /// others it will be `Ok(Some(value))`. - pub fn enter( - &mut self, - callee: FunctionIdent, - args: &[Felt], - ) -> Result { - assert_matches!(self.status, Status::Loaded, "cannot start executing a function when the emulator is already started without calling stop() or reset() first"); - - let fun = self - .functions - .get(&callee) - .cloned() - .ok_or(EmulationError::UndefinedFunction(callee))?; - self.status = Status::Started; - match fun { - Stub::Asm(ref function) => self.enter_function(function.clone(), args), - Stub::Native(function) => { - let mut function = function.borrow_mut(); - function(self, args)?; - Ok(EmulatorEvent::ExitFunction(callee)) - } - } - } - - /// Stage a MASM IR function for execution by the emulator, placing the given arguments on the - /// operand stack in FIFO order, then immediately suspending execution until the next resumption. - #[inline] - fn enter_function( - &mut self, - function: Arc, - args: &[Felt], - ) -> Result { - // Place the arguments on the operand stack - //assert_eq!(args.len(), function.arity()); - for arg in args.iter().copied().rev() { - self.stack.push(arg); - } - - // Schedule `function` - let name = function.name; - let fp = self.locals[&name]; - let state = Activation::new(function, fp); - self.callstack.push(state); - - self.status = Status::Suspended; - - Ok(EmulatorEvent::Suspended) - } - - /// Resume execution when the emulator suspended due to a breakpoint - #[inline] - pub fn resume(&mut self) -> Result { - assert_matches!( - self.status, - Status::Suspended, - "cannot resume the emulator from any state other than suspended" - ); - self.run() - } -} - -/// Pops the top element off the stack -macro_rules! pop { - ($emu:ident) => { - $emu.stack.pop().expect("operand stack is empty") - }; - - ($emu:ident, $msg:literal) => { - $emu.stack.pop().expect($msg) - }; - - ($emu:ident, $msg:literal, $($arg:expr),+) => { - match $emu.stack.pop() { - Some(value) => value, - None => panic!($msg, $($arg),*), - } - } -} - -/// Pops the top word off the stack -macro_rules! popw { - ($emu:ident) => { - $emu.stack.popw().expect("operand stack does not contain a full word") - }; - - ($emu:ident, $msg:literal) => { - $emu.stack.popw().expect($msg) - }; - - ($emu:ident, $msg:literal, $($arg:expr),+) => {{ - match $emu.stack.popw() { - Some(value) => value, - None => panic!($msg, $($arg),*), - } - }} -} - -/// Pops the top two elements off the stack, returning them in order of appearance -macro_rules! pop2 { - ($emu:ident) => {{ - let b = pop!($emu); - let a = pop!($emu); - (b, a) - }}; -} - -/// Pops a u32 value from the top of the stack, and asserts if it is out of range -macro_rules! pop_u32 { - ($emu:ident) => {{ - let value = pop!($emu).as_int(); - assert!(value < 2u64.pow(32), "assertion failed: {value} is not a valid u32, value is out of range"); - value as u32 - }}; - - ($emu:ident, $format:literal $(, $args:expr)*) => {{ - let value = pop!($emu).as_int(); - assert!(value < 2u64.pow(32), $format, value, $($args),*); - value as u32 - }} -} - -/// Pops a pointer value from the top of the stack, and asserts if it is not a valid boolean -macro_rules! pop_addr { - ($emu:ident) => {{ - let addr = pop_u32!($emu, "expected valid 32-bit address, got {}") as usize; - assert!(addr < $emu.memory.len(), "out of bounds memory access"); - addr - }}; -} - -/// Pops a boolean value from the top of the stack, and asserts if it is not a valid boolean -macro_rules! pop_bool { - ($emu:ident) => {{ - let value = pop!($emu).as_int(); - assert!( - value < 2, - "assertion failed: {value} is not a valid boolean, value must be either 1 or 0" - ); - value == 1 - }}; -} - -/// Applies a binary operator that produces a result of the same input type: -/// -/// 1. The top two elements of the stack -/// 2. The top element of the stack and an immediate. -macro_rules! binop { - ($emu:ident, $op:ident) => {{ - use core::ops::*; - let b = pop!($emu); - let a = pop!($emu); - $emu.stack.push(a.$op(b)); - }}; - - ($emu:ident, $op:ident, $imm:expr) => {{ - use core::ops::*; - let a = pop!($emu); - $emu.stack.push(a.$op($imm)); - }}; -} - -/// Applies a binary operator to two u32 values, either: -/// -/// 1. The top two elements of the stack -/// 2. The top element of the stack and an immediate. -macro_rules! binop32 { - ($emu:ident, $op:ident) => {{ - #[allow(unused)] - use core::ops::*; - let b = pop_u32!($emu); - let a = pop_u32!($emu); - $emu.stack.push_u32(a.$op(b)); - }}; - - ($emu:ident, $op:ident, $imm:expr) => {{ - #[allow(unused)] - use core::ops::*; - let a = pop_u32!($emu); - $emu.stack.push_u32(a.$op($imm)); - }}; -} - -/// Applies a checked binary operator to two u32 values, either: -/// -/// 1. The top two elements of the stack -/// 2. The top element of the stack and an immediate. -macro_rules! binop_unchecked_u32 { - ($emu:ident, $op:ident) => {{ - #[allow(unused)] - use core::ops::*; - let b = pop!($emu); - let a = pop!($emu); - $emu.stack.push(Felt::new(a.as_int().$op(b.as_int()))); - }}; - - ($emu:ident, $op:ident, $imm:expr) => {{ - #[allow(unused)] - use core::ops::*; - let a = pop!($emu); - $emu.stack.push(Felt::new(a.as_int().$op($imm))); - }}; -} - -/// Applies an overflowing binary operator to two u32 values, either: -/// -/// 1. The top two elements of the stack -/// 2. The top element of the stack and an immediate. -macro_rules! binop_overflowing_u32 { - ($emu:ident, $op:ident) => {{ - paste::paste! { - binop_overflowing_u32_impl!($emu, []); - } - }}; - - ($emu:ident, $op:ident, $imm:expr) => {{ - paste::paste! { - binop_overflowing_u32_impl!($emu, [], $imm); - } - }}; -} - -#[doc(hidden)] -macro_rules! binop_overflowing_u32_impl { - ($emu:ident, $op:ident) => {{ - #[allow(unused)] - use core::ops::*; - let b = pop_u32!($emu); - let a = pop_u32!($emu); - let (result, overflowed) = a.$op(b); - $emu.stack.push_u32(result); - $emu.stack.push_u8(overflowed as u8); - }}; - - ($emu:ident, $op:ident, $imm:expr) => {{ - #[allow(unused)] - use core::ops::*; - let a = pop_u32!($emu); - let (result, overflowed) = a.$op($imm); - $emu.stack.push_u32(result); - $emu.stack.push_u8(overflowed as u8); - }}; -} - -/// Applies a wrapping binary operator to two u32 values, either: -/// -/// 1. The top two elements of the stack -/// 2. The top element of the stack and an immediate. -macro_rules! binop_wrapping_u32 { - ($emu:ident, $op:ident) => {{ - paste::paste! { - binop_wrapping_u32_impl!($emu, []); - } - }}; - - ($emu:ident, $op:ident, $imm:expr) => {{ - paste::paste! { - binop_wrapping_u32_impl!($emu, [], $imm); - } - }}; -} - -#[doc(hidden)] -macro_rules! binop_wrapping_u32_impl { - ($emu:ident, $op:ident) => {{ - #[allow(unused)] - use core::ops::*; - let b = pop_u32!($emu); - let a = pop_u32!($emu); - $emu.stack.push_u32(a.$op(b)); - }}; - - ($emu:ident, $op:ident, $imm:expr) => {{ - #[allow(unused)] - use core::ops::*; - let a = pop_u32!($emu); - $emu.stack.push_u32(a.$op($imm)); - }}; -} - -/// Applies a binary comparison operator, to either: -/// -/// 1. The top two elements of the stack -/// 2. The top element of the stack and an immediate. -macro_rules! comparison { - ($emu:ident, $op:ident) => {{ - let b = pop!($emu).as_int(); - let a = pop!($emu).as_int(); - let result: bool = a.$op(&b); - $emu.stack.push_u8(result as u8); - }}; - - ($emu:ident, $op:ident, $imm:expr) => {{ - let a = pop!($emu).as_int(); - let result: bool = a.$op(&$imm); - $emu.stack.push_u8(result as u8); - }}; -} - -impl Emulator { - /// Step the emulator forward one cycle, returning the type of event produced - /// during that cycle, or an error. - pub fn step(&mut self) -> Result { - match self - .breakpoints - .handle_event(EmulatorEvent::CycleStart(self.clk), self.current_ip()) - { - Some(bp) => { - self.status = Status::Suspended; - Ok(EmulatorEvent::Breakpoint(bp)) - } - None => match self.run_once() { - Ok(EmulatorEvent::Stopped) => { - self.status = Status::Stopped; - Ok(EmulatorEvent::Stopped) - } - suspended @ Ok(_) => { - self.status = Status::Suspended; - suspended - } - Err(err) => { - self.status = Status::Faulted(err.clone()); - Err(err) - } - }, - } - } - - /// Step the emulator forward one step, but stepping past any nested blocks or function calls, - /// returning the type of event produced during that cycle, or an error. - pub fn step_over(&mut self) -> Result { - match self.step_over.take() { - None => self.step(), - Some(ip) => { - self.breakpoints.set(Breakpoint::At(ip)); - match self.run() { - Ok(EmulatorEvent::Stopped) => { - self.status = Status::Stopped; - Ok(EmulatorEvent::Stopped) - } - Ok(EmulatorEvent::Breakpoint(bp)) | Err(EmulationError::BreakpointHit(bp)) => { - self.status = Status::Suspended; - if self.current_ip().map(|ix| ix.ip) == Some(ip) { - return Ok(EmulatorEvent::Suspended); - } - Ok(EmulatorEvent::Breakpoint(bp)) - } - Ok(event) => panic!( - "unexpected event produced by emulator loop when stepping over: {event:?}" - ), - Err(err) => { - self.status = Status::Faulted(err.clone()); - Err(err) - } - } - } - } - } - - /// Step the emulator forward until control returns from the current function. - pub fn step_out(&mut self) -> Result { - let current_function = self.current_function(); - self.breakpoints.break_on_return(true); - match self.run() { - Ok(EmulatorEvent::Stopped) => { - self.status = Status::Stopped; - Ok(EmulatorEvent::Stopped) - } - Ok(EmulatorEvent::Breakpoint(bp)) | Err(EmulationError::BreakpointHit(bp)) => { - self.status = Status::Suspended; - if self.current_function() == current_function { - return Ok(EmulatorEvent::Suspended); - } - Ok(EmulatorEvent::Breakpoint(bp)) - } - Ok(event) => { - panic!("unexpected event produced by emulator loop when stepping over: {event:?}") - } - Err(err) => { - self.status = Status::Faulted(err.clone()); - Err(err) - } - } - } - - /// Run the emulator until all calls are completed, the cycle budget is exhausted, - /// or a breakpoint is hit. - /// - /// It is expected that the caller has set up the operand stack with the correct - /// number of arguments. If not, undefined behavior (from the perspective of the - /// MASM program) will result. - #[inline(never)] - fn run(&mut self) -> Result { - // This is the core interpreter loop for MASM IR, it runs until one of the - // following occurs: - // - // * We run out of code to execute, i.e. the function is returning normally - // * Execution was explicitly aborted from within the function - // * Execution traps due to a MASM invariant being violated, indicating the - // code is malformed. - // * Execution traps due to a runtime system error, e.g. out of memory - // * Execution traps due to exceeding the predefined execution budget - // * Execution breaks due to a breakpoint - let mut event = self.step()?; - loop { - match event { - // We should suspend when encountering these events - event @ EmulatorEvent::Breakpoint(_) => break Ok(event), - event @ EmulatorEvent::Stopped => break Ok(event), - ev => { - // We must handle catching certain breakpoints when using this event loop - match self.breakpoints.handle_event(ev, self.current_ip()) { - Some(bp) => break Ok(EmulatorEvent::Breakpoint(bp)), - None => match ev { - // There was no code remaining in the current function, effectively - // returning from it. Since no instructions were dispatched, we don't - // count the cycle, and resume immediately at the continuation point - // in the caller - EmulatorEvent::ExitFunction(_) => { - if self.callstack.is_empty() { - break Ok(EmulatorEvent::Stopped); - } - event = self.run_once()?; - continue; - } - _ => { - event = self.step()?; - } - }, - } - } - } - } - } - - #[inline(never)] - fn run_once(&mut self) -> Result { - const U32_P: u64 = 2u64.pow(32); - - // If there are no more activation records, we're done - if self.callstack.is_empty() { - return Ok(EmulatorEvent::Stopped); - } - - // Terminate execution early if we reach a predetermined number of cycles - self.clk += 1; - if self.clk > self.clk_limit { - return Err(EmulationError::CycleBudgetExceeded); - } - - let mut state = self.callstack.pop().unwrap(); - let current_function = state.function().name; - - // Reset the next instruction to break at when stepping over instructions - self.step_over = None; - - // If we have breakpoints set that require it, we may need to - // break execution before executing the instruction that is pending - if self.breakpoints.break_on_return || self.breakpoints.has_break_on_reached() { - match state.peek() { - Some(Instruction { ip, .. }) - if self.breakpoints.should_break_at(ip.block, ip.index) => - { - self.callstack.push(state); - return Ok(EmulatorEvent::Breakpoint(BreakpointEvent::Reached(ip))); - } - None if self.breakpoints.break_on_return => { - self.callstack.push(state); - self.breakpoints.break_on_return(false); - return Ok(EmulatorEvent::Breakpoint(BreakpointEvent::StepOut)); - } - _ => (), - } - } - - // Advance the instruction pointer, returning the instruction - // that it previously pointed to, along with what, if any, - // control flow effect occurred to reach it - let ix_with_op = state.next(); - if let Some(ix_with_op) = ix_with_op { - match ix_with_op.op { - Op::Padw => { - self.stack.padw(); - } - Op::Push(v) => { - self.stack.push(v); - } - Op::Push2([a, b]) => { - self.stack.push(a); - self.stack.push(b); - } - Op::Pushw(word) => { - self.stack.pushw(word); - } - Op::PushU8(i) => { - self.stack.push_u8(i); - } - Op::PushU16(i) => { - self.stack.push_u16(i); - } - Op::PushU32(i) => { - self.stack.push_u32(i); - } - Op::Drop => { - self.stack.drop(); - } - Op::Dropw => { - self.stack.dropw(); - } - Op::Dup(pos) => { - self.stack.dup(pos as usize); - } - Op::Dupw(pos) => { - self.stack.dupw(pos as usize); - } - Op::Swap(pos) => { - self.stack.swap(pos as usize); - } - Op::Swapw(pos) => { - self.stack.swapw(pos as usize); - } - Op::Movup(pos) => { - self.stack.movup(pos as usize); - } - Op::Movupw(pos) => { - self.stack.movupw(pos as usize); - } - Op::Movdn(pos) => { - self.stack.movdn(pos as usize); - } - Op::Movdnw(pos) => { - self.stack.movdnw(pos as usize); - } - Op::Cswap => { - let cond = pop_bool!(self); - if cond { - self.stack.swap(1); - } - } - Op::Cswapw => { - let cond = pop_bool!(self); - if cond { - self.stack.swapw(1); - } - } - Op::Cdrop => { - let cond = pop_bool!(self); - let (b, a) = pop2!(self); - if cond { - self.stack.push(b); - } else { - self.stack.push(a); - } - } - Op::Cdropw => { - let cond = pop_bool!(self); - let b = popw!(self); - let a = popw!(self); - if cond { - self.stack.pushw(b); - } else { - self.stack.pushw(a); - } - } - Op::Assert => { - let cond = pop_bool!(self); - assert!(cond, "assertion failed: expected true, got false"); - } - Op::Assertz => { - let cond = pop_bool!(self); - assert!(!cond, "assertion failed: expected false, got true"); - } - Op::AssertEq => { - let (b, a) = pop2!(self); - assert_eq!(a, b, "equality assertion failed"); - } - Op::AssertEqw => { - let b = popw!(self); - let a = popw!(self); - assert_eq!(a, b, "equality assertion failed"); - } - Op::LocAddr(id) => { - let addr = state.fp() + id.as_usize() as u32; - debug_assert!(addr < self.memory.len() as u32); - self.stack.push_u32(addr * 16); - } - Op::LocStore(id) => { - let addr = (state.fp() + id.as_usize() as u32) as usize; - debug_assert!(addr < self.memory.len()); - let value = pop!(self); - self.memory[addr][0] = value; - return Ok(EmulatorEvent::MemoryWrite { - addr: addr as u32, - size: 4, - }); - } - Op::LocStorew(id) => { - let addr = (state.fp() + id.as_usize() as u32) as usize; - assert!(addr < self.memory.len() - 4, "out of bounds memory access"); - let word = self - .stack - .peekw() - .expect("operand stack does not contain a full word"); - self.memory[addr] = word; - return Ok(EmulatorEvent::MemoryWrite { - addr: addr as u32, - size: 16, - }); - } - Op::MemLoad => { - let addr = pop_addr!(self); - self.stack.push(self.memory[addr][0]); - } - Op::MemLoadOffset => { - let offset = pop_u32!(self) as usize; - assert!(offset < 4, "expected valid element offset, got {offset}"); - let addr = pop_addr!(self); - self.stack.push(self.memory[addr][offset]); - } - Op::MemLoadImm(addr) => { - let addr = addr as usize; - assert!(addr < self.memory.len(), "out of bounds memory access"); - self.stack.push(self.memory[addr][0]); - } - Op::MemLoadOffsetImm(addr, offset) => { - let addr = addr as usize; - let offset = offset as usize; - assert!(addr < self.memory.len(), "out of bounds memory access"); - self.stack.push(self.memory[addr][offset]); - } - Op::MemLoadw => { - let addr = pop_addr!(self); - self.stack.dropw(); - self.stack.pushw(self.memory[addr]); - } - Op::MemLoadwImm(addr) => { - let addr = addr as usize; - assert!(addr < self.memory.len() - 4, "out of bounds memory access"); - self.stack.dropw(); - self.stack.pushw(self.memory[addr]); - } - Op::MemStore => { - let addr = pop_addr!(self); - let value = pop!(self); - self.memory[addr][0] = value; - self.callstack.push(state); - return Ok(EmulatorEvent::MemoryWrite { - addr: addr as u32, - size: 4, - }); - } - Op::MemStoreOffset => { - let offset = pop_u32!(self); - assert!(offset < 4, "expected valid element offset, got {offset}"); - let addr = pop_addr!(self); - let value = pop!(self); - let offset = offset as usize; - self.memory[addr][offset] = value; - self.callstack.push(state); - return Ok(EmulatorEvent::MemoryWrite { - addr: addr as u32, - size: 4, - }); - } - Op::MemStoreImm(addr) => { - let addr = addr as usize; - assert!(addr < self.memory.len(), "out of bounds memory access"); - let value = self.stack.pop().expect("operand stack is empty"); - self.memory[addr][0] = value; - self.callstack.push(state); - return Ok(EmulatorEvent::MemoryWrite { - addr: addr as u32, - size: 4, - }); - } - Op::MemStoreOffsetImm(addr, offset) => { - let addr = addr as usize; - let offset = offset as usize; - assert!(addr < self.memory.len(), "out of bounds memory access"); - let value = self.stack.pop().expect("operand stack is empty"); - self.memory[addr][offset] = value; - self.callstack.push(state); - return Ok(EmulatorEvent::MemoryWrite { - addr: addr as u32, - size: 4, - }); - } - Op::MemStorew => { - let addr = pop_addr!(self); - let word = self - .stack - .peekw() - .expect("operand stack does not contain a full word"); - self.memory[addr] = word; - self.callstack.push(state); - return Ok(EmulatorEvent::MemoryWrite { - addr: addr as u32, - size: 16, - }); - } - Op::MemStorewImm(addr) => { - let addr = addr as usize; - assert!(addr < self.memory.len() - 4, "out of bounds memory access"); - let word = self - .stack - .peekw() - .expect("operand stack does not contain a full word"); - self.memory[addr] = word; - self.callstack.push(state); - return Ok(EmulatorEvent::MemoryWrite { - addr: addr as u32, - size: 16, - }); - } - Op::If(then_blk, else_blk) => { - self.step_over = Some(state.ip()); - let cond = pop_bool!(self); - let dest = if cond { - state.enter_block(then_blk); - then_blk - } else { - state.enter_block(else_blk); - else_blk - }; - self.callstack.push(state); - return Ok(EmulatorEvent::Jump(dest)); - } - Op::While(body_blk) => { - self.step_over = Some(state.ip()); - let cond = pop_bool!(self); - if cond { - state.enter_while_loop(body_blk); - self.callstack.push(state); - return Ok(EmulatorEvent::EnterLoop(body_blk)); - } - } - Op::Repeat(n, body_blk) => { - self.step_over = Some(state.ip()); - state.repeat_block(body_blk, n); - self.callstack.push(state); - return Ok(EmulatorEvent::EnterLoop(body_blk)); - } - Op::Exec(callee) => { - let fun = self - .functions - .get(&callee) - .cloned() - .ok_or(EmulationError::UndefinedFunction(callee))?; - self.step_over = Some(state.ip()); - match fun { - Stub::Asm(ref function) => { - let fp = self.locals[&function.name]; - let callee_state = Activation::new(function.clone(), fp); - // Suspend caller and scheduled callee next - self.callstack.push(state); - self.callstack.push(callee_state); - return Ok(EmulatorEvent::EnterFunction(function.name)); - } - Stub::Native(_function) => unimplemented!(), - } - } - Op::Syscall(_callee) => unimplemented!(), - Op::Add => binop!(self, add), - Op::AddImm(imm) => binop!(self, add, imm), - Op::Sub => binop!(self, sub), - Op::SubImm(imm) => binop!(self, sub, imm), - Op::Mul => binop!(self, mul), - Op::MulImm(imm) => binop!(self, mul, imm), - Op::Div => binop!(self, div), - Op::DivImm(imm) => binop!(self, div, imm), - Op::Neg => { - let a = self.stack.pop().expect("operand stack is empty"); - self.stack.push(-a); - } - Op::Inv => { - let a = self.stack.pop().expect("operand stack is empty"); - self.stack.push(a.inv()); - } - Op::Incr => binop!(self, add, Felt::ONE), - Op::Pow2 => { - let a = pop!(self).as_int(); - assert!( - a < 64, - "invalid power of two: expected {a} to be a value less than 64" - ); - let two = Felt::new(2); - self.stack.push(two.exp(a)); - } - Op::Exp => { - let (b, a) = pop2!(self); - let b = b.as_int(); - assert!( - b < 64, - "invalid power of two: expected {b} to be a value less than 64" - ); - self.stack.push(a.exp(b)); - } - Op::ExpImm(pow) => { - let pow = pow as u64; - let a = pop!(self); - assert!( - pow < 64, - "invalid power of two: expected {pow} to be a value less than 64" - ); - self.stack.push(a.exp(pow)); - } - Op::Not => { - let a = pop_bool!(self); - self.stack.push_u8(!a as u8); - } - Op::And => { - let b = pop_bool!(self); - let a = pop_bool!(self); - self.stack.push_u8((b & a) as u8); - } - Op::AndImm(b) => { - let a = pop_bool!(self); - self.stack.push_u8((a & b) as u8); - } - Op::Or => { - let b = pop_bool!(self); - let a = pop_bool!(self); - self.stack.push_u8((b | a) as u8); - } - Op::OrImm(b) => { - let a = pop_bool!(self); - self.stack.push_u8((a | b) as u8); - } - Op::Xor => { - let b = pop_bool!(self); - let a = pop_bool!(self); - self.stack.push_u8((b ^ a) as u8); - } - Op::XorImm(b) => { - let a = pop_bool!(self); - self.stack.push_u8((a ^ b) as u8); - } - Op::Eq => comparison!(self, eq), - Op::EqImm(imm) => comparison!(self, eq, imm.as_int()), - Op::Neq => comparison!(self, ne), - Op::NeqImm(imm) => comparison!(self, ne, imm.as_int()), - Op::Gt => comparison!(self, gt), - Op::GtImm(imm) => comparison!(self, gt, imm.as_int()), - Op::Gte => comparison!(self, ge), - Op::GteImm(imm) => comparison!(self, ge, imm.as_int()), - Op::Lt => comparison!(self, lt), - Op::LtImm(imm) => comparison!(self, lt, imm.as_int()), - Op::Lte => comparison!(self, le), - Op::LteImm(imm) => comparison!(self, le, imm.as_int()), - Op::IsOdd => { - let a = pop!(self).as_int(); - self.stack.push_u8((a % 2 == 0) as u8); - } - Op::Eqw => { - let b = popw!(self); - let a = popw!(self); - self.stack.push_u8((a == b) as u8); - } - Op::Clk => { - self.stack.push(Felt::new(self.clk as u64)); - } - Op::U32Test => { - let top = self.stack.peek().expect("operand stack is empty").as_int(); - self.stack.push_u8((top < U32_P) as u8); - } - Op::U32Testw => { - let word = self.stack.peekw().expect("operand stack is empty"); - let is_true = word.iter().all(|elem| elem.as_int() < U32_P); - self.stack.push_u8(is_true as u8); - } - Op::U32Assert => { - let top = self.stack.peek().expect("operand stack is empty").as_int(); - assert!(top < U32_P, "assertion failed: {top} is larger than 2^32"); - } - Op::U32Assert2 => { - let a = self.stack.peek().expect("operand stack is empty").as_int(); - let b = self.stack.peek().expect("operand stack is empty").as_int(); - assert!(a < U32_P, "assertion failed: {a} is larger than 2^32"); - assert!(b < U32_P, "assertion failed: {b} is larger than 2^32"); - } - Op::U32Assertw => { - let word = self.stack.peekw().expect("operand stack is empty"); - for elem in word.into_iter() { - assert!( - elem.as_int() < U32_P, - "assertion failed: {elem} is larger than 2^32" - ); - } - } - Op::U32Cast => { - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a % U32_P)); - } - Op::U32Split => { - let a = pop!(self).as_int(); - let hi = a / U32_P; - let lo = a % U32_P; - self.stack.push(Felt::new(lo)); - self.stack.push(Felt::new(hi)); - } - Op::U32OverflowingAdd => binop_overflowing_u32!(self, add), - Op::U32OverflowingAddImm(imm) => binop_overflowing_u32!(self, add, imm), - Op::U32WrappingAdd => binop_wrapping_u32!(self, add), - Op::U32WrappingAddImm(imm) => binop_wrapping_u32!(self, add, imm), - Op::U32OverflowingAdd3 => todo!(), - Op::U32WrappingAdd3 => todo!(), - Op::U32OverflowingSub => binop_overflowing_u32!(self, sub), - Op::U32OverflowingSubImm(imm) => binop_overflowing_u32!(self, sub, imm), - Op::U32WrappingSub => binop_wrapping_u32!(self, sub), - Op::U32WrappingSubImm(imm) => binop_wrapping_u32!(self, sub, imm), - Op::U32OverflowingMul => binop_overflowing_u32!(self, mul), - Op::U32OverflowingMulImm(imm) => binop_overflowing_u32!(self, mul, imm), - Op::U32WrappingMul => binop_wrapping_u32!(self, mul), - Op::U32WrappingMulImm(imm) => binop_wrapping_u32!(self, mul, imm), - Op::U32OverflowingMadd => { - let b = pop_u32!(self) as u64; - let a = pop_u32!(self) as u64; - let c = pop_u32!(self) as u64; - let result = a * b + c; - let d = result % 2u64.pow(32); - let e = result / 2u64.pow(32); - self.stack.push(Felt::new(d)); - self.stack.push(Felt::new(e)); - } - Op::U32WrappingMadd => { - let b = pop_u32!(self) as u64; - let a = pop_u32!(self) as u64; - let c = pop_u32!(self) as u64; - let d = (a * b + c) % 2u64.pow(32); - self.stack.push(Felt::new(d)); - } - Op::U32Div => binop_unchecked_u32!(self, div), - Op::U32DivImm(imm) => binop_unchecked_u32!(self, div, imm as u64), - Op::U32Mod => { - let b = pop!(self).as_int(); - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a % b)); - } - Op::U32ModImm(imm) => { - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a % imm as u64)); - } - Op::U32DivMod => { - let b = pop!(self).as_int(); - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a / b)); - self.stack.push(Felt::new(a % b)); - } - Op::U32DivModImm(b) => { - let b = b as u64; - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a / b)); - self.stack.push(Felt::new(a % b)); - } - Op::U32And => binop32!(self, bitand), - Op::U32Or => binop32!(self, bitor), - Op::U32Xor => binop32!(self, bitxor), - Op::U32Not => { - let a = pop_u32!(self); - self.stack.push_u32(!a); - } - Op::U32Shl => binop_wrapping_u32!(self, shl), - Op::U32ShlImm(imm) => binop_wrapping_u32!(self, shl, imm), - Op::U32Shr => binop_wrapping_u32!(self, shr), - Op::U32ShrImm(imm) => binop_wrapping_u32!(self, shr, imm), - Op::U32Rotl => { - let b = pop_u32!(self); - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a.rotate_left(b))); - } - Op::U32RotlImm(imm) => { - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a.rotate_left(imm))); - } - Op::U32Rotr => { - let b = pop_u32!(self); - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a.rotate_right(b))); - } - Op::U32RotrImm(imm) => { - let a = pop!(self).as_int(); - self.stack.push(Felt::new(a.rotate_right(imm))); - } - Op::U32Popcnt => { - let a = pop!(self).as_int(); - self.stack.push_u32(a.count_ones()); - } - Op::U32Gt => comparison!(self, gt), - Op::U32Gte => comparison!(self, ge), - Op::U32Lt => comparison!(self, lt), - Op::U32Lte => comparison!(self, le), - Op::U32Min => { - let b = pop!(self).as_int(); - let a = pop!(self).as_int(); - self.stack.push(Felt::new(cmp::min(a, b))); - } - Op::U32Max => { - let b = pop!(self).as_int(); - let a = pop!(self).as_int(); - self.stack.push(Felt::new(cmp::max(a, b))); - } - op => unimplemented!("missing opcode implementation for {op:?}"), - } - - match ix_with_op.effect { - ControlEffect::Repeat(_) | ControlEffect::Loopback => { - self.callstack.push(state); - return Ok(EmulatorEvent::EnterLoop(ix_with_op.ip.block)); - } - ControlEffect::Enter => { - self.callstack.push(state); - return Ok(EmulatorEvent::Jump(ix_with_op.ip.block)); - } - ControlEffect::Exit => { - self.callstack.push(state); - return Ok(EmulatorEvent::Jump(ix_with_op.ip.block)); - } - ControlEffect::None => (), - } - - // Suspend the current activation record - self.callstack.push(state); - - Ok(EmulatorEvent::Suspended) - } else { - // No more code left in the current function - Ok(EmulatorEvent::ExitFunction(current_function)) - } - } -} diff --git a/codegen/masm/src/lib.rs b/codegen/masm/src/lib.rs deleted file mode 100644 index 934a2485f..000000000 --- a/codegen/masm/src/lib.rs +++ /dev/null @@ -1,129 +0,0 @@ -#![feature(array_windows)] -#![feature(is_sorted)] - -mod codegen; -mod convert; -mod emulator; -mod masm; -#[cfg(test)] -mod tests; - -pub use self::convert::ConvertHirToMasm; -pub use self::emulator::{ - Breakpoint, BreakpointEvent, CallFrame, DebugInfo, DebugInfoWithStack, EmulationError, - Emulator, EmulatorEvent, InstructionPointer, WatchMode, Watchpoint, WatchpointId, -}; -pub use self::masm::*; - -use miden_hir as hir; -use midenc_session::Session; - -/// 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; - -/// [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, -/// and can be emitted directly to textual Miden Assembly. -/// -/// The [MasmCompiler] is designed to compile a [miden_hir::Program] -/// -/// can be used to take a linked [miden_hir::Program] and -/// compile it to MASM IR, an intermediate representation of Miden Assembly -/// used within the compiler. -pub struct MasmCompiler<'a> { - session: &'a Session, - analyses: hir::pass::AnalysisManager, -} -impl<'a> MasmCompiler<'a> { - pub fn new(session: &'a Session) -> Self { - Self { - session, - analyses: hir::pass::AnalysisManager::new(), - } - } - - /// Compile an [hir::Program] that has been linked and is ready to be compiled. - pub fn compile(&mut self, mut input: Box) -> CompilerResult> { - use miden_hir::pass::{ConversionPass, ModuleRewritePassAdapter, RewritePass, RewriteSet}; - use miden_hir_transform as transforms; - - let mut rewrites = RewriteSet::default(); - rewrites.push(ModuleRewritePassAdapter::new( - transforms::SplitCriticalEdges, - )); - rewrites.push(ModuleRewritePassAdapter::new(transforms::Treeify)); - rewrites.push(ModuleRewritePassAdapter::new(transforms::InlineBlocks)); - - let modules = input.modules_mut().take(); - for mut module in modules.into_iter() { - rewrites.apply(&mut module, &mut self.analyses, self.session)?; - input.modules_mut().insert(module); - } - - let mut convert_to_masm = ConvertHirToMasm::::default(); - let mut program = 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) - .expect("undefined intrinsics module"), - )); - program.insert(Box::new( - intrinsics::load("intrinsics::i32", &self.session.codemap) - .expect("undefined intrinsics module"), - )); - - Ok(program) - } - - /// Compile a single [hir::Module] as a program. - /// - /// It is assumed that the given module has been validated, and that all necessary - /// rewrites have been applied. If one of these invariants is not upheld, compilation - /// may fail. - pub fn compile_module(&mut self, input: Box) -> CompilerResult> { - let program = hir::ProgramBuilder::new(&self.session.diagnostics) - .with_module(input)? - .link()?; - - self.compile(program) - } - - /// Compile a set of [hir::Module] as a program. - /// - /// It is assumed that the given modules have been validated, and that all necessary - /// rewrites have been applied. If one of these invariants is not upheld, compilation - /// may fail. - pub fn compile_modules>>( - &mut self, - input: I, - ) -> CompilerResult> { - let mut builder = hir::ProgramBuilder::new(&self.session.diagnostics); - for module in input.into_iter() { - builder.add_module(module)?; - } - - let program = builder.link()?; - - self.compile(program) - } -} diff --git a/codegen/masm/src/masm/function.rs b/codegen/masm/src/masm/function.rs deleted file mode 100644 index fe51fee5d..000000000 --- a/codegen/masm/src/masm/function.rs +++ /dev/null @@ -1,296 +0,0 @@ -use std::fmt; -use std::sync::Arc; - -use cranelift_entity::EntityRef; -use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListAtomicLink}; -use miden_diagnostics::{SourceSpan, Spanned}; -use miden_hir::{AttributeSet, FunctionIdent, Ident, Signature, Type}; -use rustc_hash::FxHashMap; -use smallvec::SmallVec; - -use super::*; - -intrusive_adapter!(pub FunctionListAdapter = Box: Function { link: LinkedListAtomicLink }); -intrusive_adapter!(pub FrozenFunctionListAdapter = Arc: Function { link: LinkedListAtomicLink }); - -/// This represents a function in Miden Assembly -#[derive(Spanned)] -pub struct Function { - link: LinkedListAtomicLink, - #[span] - pub span: SourceSpan, - /// The attributes associated with this function - pub attrs: AttributeSet, - /// The name of this function - pub name: FunctionIdent, - /// The type signature of this function - pub signature: Signature, - /// The [Region] which forms the body of this function - pub body: Region, - /// Locals allocated for this function - locals: SmallVec<[Local; 1]>, - /// The next available local index - next_local_id: usize, -} -impl Function { - pub fn new(name: FunctionIdent, signature: Signature) -> Self { - Self { - link: Default::default(), - span: SourceSpan::UNKNOWN, - attrs: Default::default(), - name, - signature, - body: Default::default(), - locals: Default::default(), - next_local_id: 0, - } - } - - /// Returns true if this function is decorated with the `entrypoint` attribute. - pub fn is_entrypoint(&self) -> bool { - use miden_hir::symbols; - - self.attrs.has(&symbols::Entrypoint) - } - - /// Return the number of arguments expected on the operand stack - #[inline] - pub fn arity(&self) -> usize { - self.signature.arity() - } - - /// Return the number of results produced by this function - #[inline] - pub fn num_results(&self) -> usize { - self.signature.results.len() - } - - /// Allocate a new local in this function, using the provided data - /// - /// The index of the local is returned as it's identifier - pub fn alloc_local(&mut self, ty: Type) -> LocalId { - let num_words = ty.size_in_words(); - let next_id = self.next_local_id; - assert!( - (next_id + num_words) < (u8::MAX as usize), - "unable to allocate a local of type {}: unable to allocate enough local memory", - &ty - ); - let id = LocalId::new(next_id); - self.next_local_id += num_words; - let local = Local { id, ty }; - self.locals.push(local); - id - } - - /// Get the local with the given identifier - pub fn local(&self, id: LocalId) -> &Local { - self.locals - .iter() - .find(|l| l.id == id) - .expect("invalid local id") - } - - /// Return the locals allocated in this function as a slice - #[inline] - pub fn locals(&self) -> &[Local] { - self.locals.as_slice() - } - - /// Allocate a new code block in this function - #[inline(always)] - pub fn create_block(&mut self) -> BlockId { - self.body.create_block() - } - - /// Get a reference to a [Block] by [BlockId] - #[inline(always)] - pub fn block(&self, id: BlockId) -> &Block { - self.body.block(id) - } - - /// Get a mutable reference to a [Block] by [BlockId] - #[inline(always)] - pub fn block_mut(&mut self, id: BlockId) -> &mut Block { - self.body.block_mut(id) - } - - /// Return an implementation of [std::fmt::Display] for this function - pub fn display<'a, 'b: 'a>(&'b self, imports: &'b ModuleImportInfo) -> DisplayMasmFunction<'a> { - DisplayMasmFunction { - function: self, - imports, - } - } - - pub fn from_procedure_ast( - module: Ident, - proc: &miden_assembly::ast::ProcedureAst, - locals: &[FunctionIdent], - imported: &miden_assembly::ast::ModuleImports, - ) -> Box { - use miden_hir::{Linkage, Symbol}; - let id = FunctionIdent { - module, - function: Ident::with_empty_span(Symbol::intern(proc.name.as_ref())), - }; - let mut signature = Signature::new(vec![], vec![]); - if !proc.is_export { - signature.linkage = Linkage::Internal; - } - let mut function = Box::new(Self::new(id, signature)); - if proc.name.is_main() { - function.attrs.set(miden_hir::attributes::ENTRYPOINT); - } - for _ in 0..proc.num_locals { - function.alloc_local(Type::Felt); - } - - function.body = Region::from_code_body(&proc.body, locals, imported); - - function - } - - pub fn to_function_ast( - &self, - codemap: &miden_diagnostics::CodeMap, - imports: &miden_hir::ModuleImportInfo, - local_ids: &FxHashMap, - proc_ids: &FxHashMap, - ) -> miden_assembly::ast::ProcedureAst { - use miden_assembly::{ - self as masm, - ast::{ProcedureAst, SourceLocation}, - }; - - let name = masm::ProcedureName::try_from(self.name.function.as_str()) - .expect("invalid function name"); - let num_locals = u16::try_from(self.locals.len()).expect("too many locals"); - let start = codemap - .location(self) - .ok() - .map(|loc| { - SourceLocation::new(loc.line.to_usize() as u32, loc.column.to_usize() as u32) - }) - .unwrap_or_default(); - let body = self - .body - .to_code_body(codemap, imports, local_ids, proc_ids); - - ProcedureAst { - name, - docs: None, - num_locals, - body, - start, - is_export: self.signature.is_public(), - } - } -} - -impl fmt::Debug for Function { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Function") - .field("name", &self.name) - .field("signature", &self.signature) - .field("attrs", &self.attrs) - .field("locals", &self.locals) - .field("body", &self.body) - .finish() - } -} - -#[doc(hidden)] -pub struct DisplayMasmFunction<'a> { - function: &'a Function, - imports: &'a ModuleImportInfo, -} -impl<'a> fmt::Display for DisplayMasmFunction<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let visibility = if self.function.signature.is_public() { - "export" - } else { - "proc" - }; - let name = self.function.name; - match self.function.locals.len() { - 0 => { - writeln!(f, "{visibility}.{}", &name.function)?; - } - n => { - writeln!(f, "{visibility}.{}.{}", &name.function, n)?; - } - } - - writeln!( - f, - "{}", - self.function - .body - .display(Some(self.function.name), self.imports, 1) - )?; - - f.write_str("end") - } -} - -pub type FunctionList = LinkedList; -pub type FunctionListIter<'a> = intrusive_collections::linked_list::Iter<'a, FunctionListAdapter>; - -pub type FrozenFunctionList = LinkedList; -pub type FrozenFunctionListIter<'a> = - intrusive_collections::linked_list::Iter<'a, FrozenFunctionListAdapter>; - -pub(super) enum Functions { - Open(FunctionList), - Frozen(FrozenFunctionList), -} -impl Default for Functions { - fn default() -> Self { - Self::Open(Default::default()) - } -} -impl Functions { - pub fn iter(&self) -> impl Iterator + '_ { - match self { - Self::Open(ref list) => FunctionsIter::Open(list.iter()), - Self::Frozen(ref list) => FunctionsIter::Frozen(list.iter()), - } - } - - pub fn push_back(&mut self, function: Box) { - match self { - Self::Open(ref mut list) => { - list.push_back(function); - } - Self::Frozen(_) => panic!("cannot insert function into frozen module"), - } - } - - pub fn freeze(&mut self) { - if let Self::Open(ref mut functions) = self { - let mut frozen = FrozenFunctionList::default(); - - while let Some(function) = functions.pop_front() { - frozen.push_back(Arc::from(function)); - } - - *self = Self::Frozen(frozen); - } - } -} - -enum FunctionsIter<'a> { - Open(FunctionListIter<'a>), - Frozen(FrozenFunctionListIter<'a>), -} -impl<'a> Iterator for FunctionsIter<'a> { - type Item = &'a Function; - - fn next(&mut self) -> Option { - match self { - Self::Open(ref mut iter) => iter.next(), - Self::Frozen(ref mut iter) => iter.next(), - } - } -} diff --git a/codegen/masm/src/masm/import.rs b/codegen/masm/src/masm/import.rs deleted file mode 100644 index 5072fdb04..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 miden_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 deleted file mode 100644 index 0d617d7c3..000000000 --- a/codegen/masm/src/masm/intrinsics.rs +++ /dev/null @@ -1,30 +0,0 @@ -use miden_diagnostics::{CodeMap, FileName}; - -use super::Module; - -const I32_INTRINSICS: &str = - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/intrinsics/i32.masm")); -const MEM_INTRINSICS: &str = - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/intrinsics/mem.masm")); - -/// This is a mapping of intrinsics module name to the raw MASM source for that module -const INTRINSICS: [(&str, &str, &str); 2] = [ - ("intrinsics::i32", I32_INTRINSICS, "i32.masm"), - ("intrinsics::mem", MEM_INTRINSICS, "mem.masm"), -]; - -/// 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 { - 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(); - match Module::parse_source_file(source_file, name, codemap) { - Ok(module) => Some(module), - Err(err) => { - panic!("unexpected syntax error in intrinsic module: {err}"); - } - } -} diff --git a/codegen/masm/src/masm/mod.rs b/codegen/masm/src/masm/mod.rs deleted file mode 100644 index 7efa6c21a..000000000 --- a/codegen/masm/src/masm/mod.rs +++ /dev/null @@ -1,96 +0,0 @@ -mod function; -pub mod intrinsics; -mod module; -mod program; -mod region; - -pub use self::function::{FrozenFunctionList, Function, FunctionList}; -pub use self::module::{FrozenModuleTree, LoadModuleError, Module, ModuleTree}; -pub use self::program::Program; -pub use self::region::{Begin, Region}; -pub use miden_hir::{ - Local, LocalId, MasmBlock as Block, MasmBlockId as BlockId, MasmImport as Import, MasmOp as Op, - ModuleImportInfo, -}; - -/// This represents a descriptor for a pointer translated from the IR into a form suitable for -/// referencing data in Miden's linear memory. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct NativePtr { - /// This is the address of the word containing the first byte of data - pub waddr: u32, - /// This is the element index of the word referenced by `waddr` containing the first byte of data - /// - /// Each element is assumed to be a 32-bit value/chunk - pub index: u8, - /// This is the byte offset into the 32-bit chunk referenced by `index` - /// - /// This offset is where the data referenced by the pointer actually starts. - pub offset: u8, - /// This is the assumed address space of the pointer value. - /// - /// This address space is unknown by default, but can be specified if known statically. - /// The address space determines whether the pointer is valid in certain contexts. For - /// example, attempting to load a pointer with address space 0 is invalid if not operating - /// in the root context. - /// - /// Currently this has no effect, but is here as we expand support for multiple memories. - pub addrspace: miden_hir::AddressSpace, -} -impl NativePtr { - /// Translates a raw pointer (assumed to be in a byte-addressable address space) to - /// a native pointer value, in the default [hir::AddressSpace]. - pub fn from_ptr(addr: u32) -> Self { - // The native word address for `addr` is derived by splitting - // the byte-addressable space into 32-bit chunks, each chunk - // belonging to a single field element. Thus, each word of the - // native address space represents 128 bits of byte-addressable - // memory. - // - // By dividing `addr` by 16, we get the word index (i.e. address) - // where the data starts. - let waddr = addr / 16; - // If our address is not word-aligned, we need to determine what - // element index contains the 32-bit chunk where the data begins - let woffset = addr % 16; - let index = (woffset / 4) as u8; - // If our address is not element-aligned, we need to determine - // what byte offset contains the first byte of the data - let offset = (woffset % 4) as u8; - Self { - waddr, - index, - offset, - addrspace: Default::default(), - } - } - - /// Returns true if this pointer is aligned to a word boundary - pub const fn is_word_aligned(&self) -> bool { - self.index == 0 && self.offset == 0 - } - - /// Returns true if this pointer is aligned to a field element boundary - pub const fn is_element_aligned(&self) -> bool { - self.offset == 0 - } - - /// Returns true if this pointer is not word or element aligned - pub const fn is_unaligned(&self) -> bool { - self.offset > 0 - } - - /// Returns the byte alignment implied by this pointer value. - /// - /// For example, a pointer to the first word in linear memory, i.e. address 0, - /// with an element index of 1, and offset of 16, is equivalent to an address - /// in byte-addressable memory of 48, which has an implied alignment of 16 bytes. - pub const fn alignment(&self) -> u32 { - 2u32.pow(self.as_ptr().trailing_zeros()) - } - - /// Converts this native pointer back to a byte-addressable pointer value - pub const fn as_ptr(&self) -> u32 { - (self.waddr * 16) + (self.index as u32 * 4) + self.offset as u32 - } -} diff --git a/codegen/masm/src/masm/module.rs b/codegen/masm/src/masm/module.rs deleted file mode 100644 index 6fb786a39..000000000 --- a/codegen/masm/src/masm/module.rs +++ /dev/null @@ -1,390 +0,0 @@ -use std::{ - collections::BTreeMap, - fmt, - path::{Path, PathBuf}, - sync::Arc, -}; - -use intrusive_collections::{intrusive_adapter, RBTree, RBTreeAtomicLink}; -use miden_assembly::ast::ModuleAst; -use miden_diagnostics::{CodeMap, SourceFile, SourceSpan}; -use miden_hir::{FunctionIdent, Ident, Symbol}; -use rustc_hash::FxHashMap; - -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)] - InvalidModulePath(#[from] miden_assembly::PathError), - #[error(transparent)] - ParseFailed(#[from] miden_assembly::ParsingError), -} - -/// 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, -/// i.e. [miden_assembly::ast::ModuleAst]. -pub struct Module { - link: RBTreeAtomicLink, - pub span: SourceSpan, - /// The name of this module, e.g. `std::math::u64` - pub name: Ident, - /// The module-scoped documentation for this module - pub docs: Option, - /// The modules to import, along with their local aliases - pub imports: ModuleImportInfo, - /// The functions defined in this module - functions: Functions, -} -impl Module { - /// Create a new, empty [Module] with the given name. - pub fn new(name: Ident) -> Self { - Self { - link: Default::default(), - span: SourceSpan::UNKNOWN, - name, - docs: None, - imports: Default::default(), - functions: Default::default(), - } - } - - /// If this module contains a function marked with the `entrypoint` attribute, - /// return the fully-qualified name of that function - pub fn entrypoint(&self) -> Option { - self.functions.iter().find_map(|f| { - if f.is_entrypoint() { - Some(f.name) - } else { - None - } - }) - } - - /// Returns true if this module contains a [Function] `name` - pub fn contains(&self, name: Ident) -> bool { - self.functions.iter().any(|f| f.name.function == name) - } - - /// Parse a [Module] from the given string - pub fn parse_source_file>( - source_file: Arc, - name: N, - codemap: &CodeMap, - ) -> Result { - use miden_assembly::LibraryPath; - - let name = name.into(); - let module = miden_assembly::Module { - path: LibraryPath::new(name.as_str())?, - ast: ModuleAst::parse(source_file.source())?, - }; - let span = source_file.source_span(); - Ok(Self::from_module(&module, span, codemap)) - } - - /// Parse a [Module] from the given file path - pub fn parse_file>( - path: P, - root_ns: Option, - codemap: &CodeMap, - ) -> Result { - let id = codemap.add_file(path)?; - let source_file = codemap.get(id).unwrap(); - let basename = source_file - .name() - .as_ref() - .file_name() - .unwrap() - .to_str() - .unwrap(); - let name = match root_ns { - None => basename.to_string(), - Some(root_ns) => format!("{}::{basename}", root_ns.as_str()), - }; - Self::parse_source_file(source_file, name.as_str(), codemap) - } - - pub fn from_module( - module: &miden_assembly::Module, - span: SourceSpan, - codemap: &CodeMap, - ) -> Self { - let name = Ident::with_empty_span(Symbol::intern(module.path.as_str())); - Self::from_module_ast_with_name(&module.ast, name, span, codemap) - } - - /// Convert a [miden_assembly::ast::ModuleAst] to a [Module] - pub fn from_module_ast_with_name>( - ast: &ModuleAst, - name: N, - span: SourceSpan, - _codemap: &CodeMap, - ) -> Self { - let module_name = name.into(); - let mut module = Self::new(module_name); - module.span = span; - module.docs = ast.docs().cloned(); - - let imported = ast.import_info().clone(); - let locals = ast - .procs() - .iter() - .map(|p| FunctionIdent { - module: module_name, - function: Ident::with_empty_span(Symbol::intern(p.name.as_ref())), - }) - .collect::>(); - - for proc in ast.procs() { - let function = Function::from_procedure_ast(module_name, proc, &locals, &imported); - module.functions.push_back(function); - } - - module - } - - /// Freezes this program, preventing further modifications - pub fn freeze(mut self: Box) -> Arc { - self.functions.freeze(); - Arc::from(self) - } - - /// Get an iterator over the functions in this module - pub fn functions(&self) -> impl Iterator + '_ { - self.functions.iter() - } - - /// Access the frozen functions list of this module, and panic if not frozen - pub fn unwrap_frozen_functions(&self) -> &FrozenFunctionList { - match self.functions { - Functions::Frozen(ref functions) => functions, - Functions::Open(_) => panic!("expected module to be frozen"), - } - } - - /// Append a function to the end of this module - /// - /// NOTE: This function will panic if the module has been frozen - pub fn push_back(&mut self, function: Box) { - self.functions.push_back(function); - } - - /// Convert this module into its [miden_assembly::Module] representation. - pub fn to_module_ast(&self, codemap: &miden_diagnostics::CodeMap) -> miden_assembly::Module { - use miden_assembly::{self as masm, ast::ModuleImports}; - - // Create module import table - let mut imported = BTreeMap::::default(); - let mut invoked = BTreeMap::::default(); - let mut proc_ids = FxHashMap::::default(); - for import in self.imports.iter() { - let path = masm::LibraryPath::new(import.name.as_str()).expect("invalid module name"); - imported.insert(import.alias.to_string(), path.clone()); - if let Some(imported_fns) = self.imports.imported(&import.alias) { - for import_fn in imported_fns.iter().copied() { - let fname = import_fn.to_string(); - let name = masm::ProcedureName::try_from(fname.as_str()) - .expect("invalid function name"); - let id = masm::ProcedureId::from_name(fname.as_str(), &path); - invoked.insert(id, (name, path.clone())); - proc_ids.insert(import_fn, id); - } - } - } - let imports = ModuleImports::new(imported, invoked); - - // Translate functions - let mut local_ids = FxHashMap::default(); - for (id, function) in self.functions.iter().enumerate() { - local_ids.insert(function.name, id as u16); - } - let mut procs = Vec::with_capacity(self.num_imported_functions()); - for function in self.functions.iter() { - procs.push(function.to_function_ast(codemap, &self.imports, &local_ids, &proc_ids)); - } - - // Construct module - let path = masm::LibraryPath::new(self.name.as_str()).expect("invalid module name"); - let ast = ModuleAst::new(procs, vec![], self.docs.clone()) - .expect("invalid module body") - .with_import_info(imports); - masm::Module { path, ast } - } - - fn num_imported_functions(&self) -> usize { - self.imports - .iter() - .map(|i| { - self.imports - .imported(&i.alias) - .map(|imported| imported.len()) - .unwrap_or(0) - }) - .sum() - } - - /// Write this module to a new file under `dir`, assuming `dir` is the root directory for a program. - /// - /// 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<()> { - use std::fs::File; - - let mut path = dir.as_ref().to_path_buf(); - assert!(path.is_dir()); - for component in self.name.as_str().split("::") { - path.push(component); - } - 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_module_ast(codemap); - out.write_fmt(format_args!("{}", &ast.ast)) - } -} -impl fmt::Display for Module { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for import in self.imports.iter() { - if import.is_aliased() { - writeln!(f, "use.{}->{}", import.name, import.alias)?; - } else { - writeln!(f, "use.{}", import.name)?; - } - } - - if !self.imports.is_empty() { - writeln!(f)?; - } - - for (i, function) in self.functions.iter().enumerate() { - if i > 0 { - writeln!(f, "\n{}", function.display(&self.imports))?; - } else { - writeln!(f, "{}", function.display(&self.imports))?; - } - } - - Ok(()) - } -} -impl midenc_session::Emit for Module { - fn name(&self) -> Option { - Some(self.name.as_symbol()) - } - fn output_type(&self) -> midenc_session::OutputType { - midenc_session::OutputType::Masm - } - fn write_to(&self, mut writer: W) -> std::io::Result<()> { - writer.write_fmt(format_args!("{}", self)) - } -} - -intrusive_adapter!(pub ModuleTreeAdapter = Box: Module { link: RBTreeAtomicLink }); -intrusive_adapter!(pub FrozenModuleTreeAdapter = Arc: Module { link: RBTreeAtomicLink }); -impl<'a> intrusive_collections::KeyAdapter<'a> for ModuleTreeAdapter { - type Key = Ident; - - #[inline] - fn get_key(&self, module: &'a Module) -> Ident { - module.name - } -} -impl<'a> intrusive_collections::KeyAdapter<'a> for FrozenModuleTreeAdapter { - type Key = Ident; - - #[inline] - fn get_key(&self, module: &'a Module) -> Ident { - module.name - } -} - -pub type ModuleTree = RBTree; -pub type ModuleTreeIter<'a> = intrusive_collections::rbtree::Iter<'a, ModuleTreeAdapter>; - -pub type FrozenModuleTree = RBTree; -pub type FrozenModuleTreeIter<'a> = - intrusive_collections::rbtree::Iter<'a, FrozenModuleTreeAdapter>; - -pub(super) enum Modules { - Open(ModuleTree), - Frozen(FrozenModuleTree), -} -impl Default for Modules { - fn default() -> Self { - Self::Open(Default::default()) - } -} -impl Modules { - pub fn iter(&self) -> impl Iterator + '_ { - match self { - Self::Open(ref tree) => ModulesIter::Open(tree.iter()), - Self::Frozen(ref tree) => ModulesIter::Frozen(tree.iter()), - } - } - - pub fn get(&self, name: &Q) -> Option<&Module> - where - Q: ?Sized + Ord, - Ident: core::borrow::Borrow, - { - match self { - Self::Open(ref tree) => tree.find(name).get(), - Self::Frozen(ref tree) => tree.find(name).get(), - } - } - - pub fn insert(&mut self, module: Box) { - match self { - Self::Open(ref mut tree) => { - tree.insert(module); - } - Self::Frozen(_) => panic!("cannot insert module into frozen program"), - } - } - - pub fn freeze(&mut self) { - if let Self::Open(ref mut modules) = self { - let mut frozen = FrozenModuleTree::default(); - - let mut open = modules.front_mut(); - while let Some(module) = open.remove() { - frozen.insert(module.freeze()); - } - - *self = Self::Frozen(frozen); - } - } -} - -enum ModulesIter<'a> { - Open(ModuleTreeIter<'a>), - Frozen(FrozenModuleTreeIter<'a>), -} -impl<'a> Iterator for ModulesIter<'a> { - type Item = &'a Module; - - fn next(&mut self) -> Option { - match self { - Self::Open(ref mut iter) => iter.next(), - Self::Frozen(ref mut iter) => iter.next(), - } - } -} diff --git a/codegen/masm/src/masm/program.rs b/codegen/masm/src/masm/program.rs deleted file mode 100644 index 15f6be516..000000000 --- a/codegen/masm/src/masm/program.rs +++ /dev/null @@ -1,296 +0,0 @@ -use core::fmt; -use std::{collections::BTreeMap, path::Path, sync::Arc}; - -use miden_hir::{self as hir, DataSegmentTable, FunctionIdent, Ident}; -use rustc_hash::FxHashMap; - -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. -#[derive(Default)] -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 top-level global initialization code for this program, if applicable - pub body: Option, -} -impl Program { - /// Create a new, empty [Program] - pub fn new() -> Self { - Self::default() - } - - /// Freezes this program, 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 - pub fn modules(&self) -> impl Iterator + '_ { - self.modules.iter() - } - - /// Access the frozen module tree of this program, and panic if not frozen - pub fn unwrap_frozen_modules(&self) -> &FrozenModuleTree { - match self.modules { - Modules::Frozen(ref modules) => modules, - Modules::Open(_) => panic!("expected program to be frozen"), - } - } - - /// Insert a module into this program - /// - /// NOTE: This function will panic if the program has been frozen - pub fn insert(&mut self, module: Box) { - self.modules.insert(module); - } - - pub fn is_executable(&self) -> bool { - self.body.is_some() - } - - pub fn is_library(&self) -> bool { - self.body.is_none() - } - - /// 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.modules.get(name) - } - - /// Returns true if this program contains a [Module] named `name` - pub fn contains(&self, name: N) -> bool - where - Ident: PartialEq, - { - self.modules.iter().any(|m| m.name == 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<()> { - use miden_assembly as masm; - - let path = path.as_ref(); - assert!(path.is_dir()); - - let program = self.to_program_ast(codemap); - program.write_to_file(path.join(masm::LibraryPath::EXEC_PATH))?; - - for module in self.modules.iter() { - module.write_to_directory(codemap, path)?; - } - - Ok(()) - } - - /// Convert this program to its [miden_assembly::ast::ProgramAst] representation - pub fn to_program_ast( - &self, - codemap: &miden_diagnostics::CodeMap, - ) -> miden_assembly::ast::ProgramAst { - use miden_assembly::{ - self as masm, - ast::{Instruction, ModuleImports, Node, ProgramAst}, - }; - - if let Some(begin) = &self.body { - // Create module import table - let mut imported = BTreeMap::::default(); - let mut invoked = BTreeMap::::default(); - let mut proc_ids = FxHashMap::::default(); - for import in begin.imports.iter() { - let path = - masm::LibraryPath::new(import.name.as_str()).expect("invalid module name"); - imported.insert(import.alias.to_string(), path.clone()); - if let Some(imported_fns) = begin.imports.imported(&import.alias) { - for import_fn in imported_fns.iter().copied() { - let name = masm::ProcedureName::try_from(import_fn.function.as_str()) - .expect("invalid function name"); - let id = masm::ProcedureId::from_name(import_fn.function.as_str(), &path); - invoked.insert(id, (name, path.clone())); - proc_ids.insert(import_fn, id); - } - } - } - let imports = ModuleImports::new(imported, invoked); - let local_ids = Default::default(); - let (nodes, _) = begin - .body - .to_code_body(codemap, &begin.imports, &local_ids, &proc_ids) - .into_parts(); - - ProgramAst::new(nodes, vec![]) - .expect("invalid program") - .with_import_info(imports) - } else if let Some(entry) = self.modules.iter().find_map(|m| m.entrypoint()) { - let entry_import = Import::try_from(entry.module).expect("invalid module name"); - let entry_module_path = - masm::LibraryPath::new(entry_import.name.as_str()).expect("invalid module path"); - let entry_id = - masm::ProcedureId::from_name(entry.function.as_str(), &entry_module_path); - let entry_name = masm::ProcedureName::try_from(entry.function.as_str()) - .expect("invalid entrypoint function name"); - let imported = - BTreeMap::from([(entry_import.alias.to_string(), entry_module_path.clone())]); - let invoked = BTreeMap::from([(entry_id, (entry_name, entry_module_path))]); - let imports = ModuleImports::new(imported, invoked); - - // TODO: Write data segments, initialize function table - let body = vec![Node::Instruction(Instruction::ExecImported(entry_id))]; - - ProgramAst::new(body, vec![]) - .expect("invalid program") - .with_import_info(imports) - } else { - todo!("0xPolygonMiden/miden-vm#1108") - } - } - - /// Load a [Program] from a `.masl` file - pub fn from_masl>( - path: P, - codemap: &miden_diagnostics::CodeMap, - ) -> Result { - use miden_assembly::MaslLibrary; - - MaslLibrary::read_from_file(path.as_ref()).map(|lib| Self::from_masl_library(&lib, codemap)) - } - - /// Load a [Program] from a MASL directory hierarchy, with the given root namespace. - pub fn from_dir, S: AsRef>( - path: P, - root_ns: S, - codemap: &miden_diagnostics::CodeMap, - ) -> Result { - use miden_assembly::{LibraryError, LibraryNamespace, MaslLibrary}; - - let root_ns = LibraryNamespace::new(root_ns.as_ref())?; - let path = path.as_ref(); - let library = MaslLibrary::read_from_dir( - path, - root_ns, - /*with_source_locations=*/ true, - Default::default(), - ) - .map_err(|err| LibraryError::file_error(path.to_str().unwrap(), &format!("{err}")))?; - - Ok(Self::from_masl_library(&library, codemap)) - } - - pub fn to_masl_library>( - &self, - root_ns: S, - codemap: &miden_diagnostics::CodeMap, - ) -> Result { - use miden_assembly::{LibraryNamespace, MaslLibrary, Version}; - use std::collections::BTreeSet; - - let ns = LibraryNamespace::new(root_ns)?; - let version = Version::default(); - let has_source_locations = false; - let mut modules = Vec::with_capacity(self.modules.iter().count()); - let mut dependencies = BTreeSet::default(); - for module in self.modules() { - for import in module.imports.iter() { - if self.modules.get(&import.name).is_some() { - continue; - } - let root = match import.name.as_str().split_once("::") { - None => LibraryNamespace::new(import.name.as_str())?, - Some((root, _)) => LibraryNamespace::new(root)?, - }; - dependencies.insert(root); - } - modules.push(module.to_module_ast(codemap)); - } - MaslLibrary::new( - ns, - version, - has_source_locations, - modules, - dependencies.into_iter().collect(), - ) - } - - /// Convert a [miden_assembly::MaslLibrary] into a [Program] - pub fn from_masl_library( - library: &miden_assembly::MaslLibrary, - codemap: &miden_diagnostics::CodeMap, - ) -> Self { - use miden_assembly::Library; - use miden_diagnostics::SourceSpan; - - let mut modules = ModuleTree::default(); - for module in library.modules() { - let module = Module::from_module(module, SourceSpan::UNKNOWN, codemap); - modules.insert(Box::new(module)); - } - - Self { - modules: Modules::Open(modules), - segments: DataSegmentTable::default(), - body: None, - } - } -} -impl From<&hir::Program> for Program { - fn from(program: &hir::Program) -> Self { - let segments = program.segments().clone(); - let body = if let Some(entry) = program.entrypoint() { - let mut begin = Begin::default(); - begin.imports.add(entry); - let entry_module = begin.imports.alias(&entry.module); - begin - .body - .block_mut(begin.body.body) - .ops - .push(Op::Exec(FunctionIdent { - module: entry_module.unwrap_or(entry.module), - function: entry.function, - })); - Some(begin) - } else { - None - }; - Self { - modules: Default::default(), - segments, - body, - } - } -} -impl fmt::Display for Program { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for module in self.modules() { - writeln!(f, "mod {}\n", &module.name)?; - writeln!(f, "{}", module)?; - } - if let Some(entry) = self.body.as_ref() { - writeln!(f, "program\n")?; - for import in entry.imports.iter() { - if import.is_aliased() { - writeln!(f, "use {}->{}", &import.name, &import.alias)?; - } else { - writeln!(f, "use {}", &import.name)?; - } - } - writeln!(f, "\n{entry}")?; - } - Ok(()) - } -} diff --git a/codegen/masm/src/masm/region.rs b/codegen/masm/src/masm/region.rs deleted file mode 100644 index 78a3b9de5..000000000 --- a/codegen/masm/src/masm/region.rs +++ /dev/null @@ -1,259 +0,0 @@ -use std::fmt; - -use cranelift_entity::PrimaryMap; -use miden_assembly::ast; -use miden_hir::FunctionIdent; -use rustc_hash::FxHashMap; -use smallvec::smallvec; - -use crate::InstructionPointer; - -use super::*; - -/// This struct represents the top-level initialization code for a [Program] -#[derive(Default)] -pub struct Begin { - /// The imports available in `body` - pub imports: ModuleImportInfo, - /// The body of the `begin` block - pub body: Region, -} -impl fmt::Display for Begin { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("begin\n")?; - - writeln!(f, "{}", self.body.display(None, &self.imports, 1))?; - - f.write_str("end") - } -} - -/// This struct represents a region of code in Miden Assembly. -/// -/// A region is a tree of blocks with isolated scope. In many -/// ways a [Region] is basically a [Function], just without any -/// identity. Additionally, a [Region] does not have local variables, -/// those must be provided by a parent [Function]. -/// -/// In short, this represents both the body of a function, and the -/// body of a `begin` block in Miden Assembly. -#[derive(Debug, Clone)] -pub struct Region { - pub body: BlockId, - pub blocks: PrimaryMap, -} -impl Default for Region { - fn default() -> Self { - let mut blocks = PrimaryMap::::default(); - let id = blocks.next_key(); - let body = blocks.push(Block { - id, - ops: smallvec![], - }); - Self { body, blocks } - } -} -impl Region { - /// Get the [BlockId] for the block which forms the body of this region - #[inline(always)] - pub const fn id(&self) -> BlockId { - self.body - } - - /// Get a reference to a [Block] by [BlockId] - #[inline] - pub fn block(&self, id: BlockId) -> &Block { - &self.blocks[id] - } - - /// Get a mutable reference to a [Block] by [BlockId] - #[inline] - pub fn block_mut(&mut self, id: BlockId) -> &mut Block { - &mut self.blocks[id] - } - - /// Get the instruction under `ip`, if valid - pub fn get(&self, ip: InstructionPointer) -> Option { - self.blocks[ip.block].ops.get(ip.index).copied() - } - - /// Allocate a new code block in this region - pub fn create_block(&mut self) -> BlockId { - let id = self.blocks.next_key(); - self.blocks.push(Block { - id, - ops: smallvec![], - }); - id - } - - /// Render the code in this region as Miden Assembly, at the specified indentation level (in units of 4 spaces) - pub fn display<'a, 'b: 'a>( - &'b self, - function: Option, - imports: &'b ModuleImportInfo, - indent: usize, - ) -> impl fmt::Display + 'a { - DisplayRegion { - region: self, - function, - imports, - indent, - } - } - - /// Convert this [Region] to a [miden_assembly::ast::CodeBody] using the provided - /// local/external function maps to handle calls present in the body of the region. - pub fn to_code_body( - &self, - codemap: &miden_diagnostics::CodeMap, - imports: &ModuleImportInfo, - local_ids: &FxHashMap, - proc_ids: &FxHashMap, - ) -> ast::CodeBody { - emit_block( - self.body, - &self.blocks, - codemap, - imports, - local_ids, - proc_ids, - ) - } - - /// Create a [Region] from a [miden_assembly::ast::CodeBody] and the set of imports - /// and local procedures which will be used to map references to procedures to their - /// fully-qualified names. - pub fn from_code_body( - code: &ast::CodeBody, - locals: &[FunctionIdent], - imported: &ast::ModuleImports, - ) -> Self { - let mut region = Self::default(); - - let body = region.body; - import_code_body(&mut region, body, code, locals, imported); - - region - } -} -impl core::ops::Index for Region { - type Output = Op; - - #[inline] - fn index(&self, ip: InstructionPointer) -> &Self::Output { - &self.blocks[ip.block].ops[ip.index] - } -} - -#[doc(hidden)] -pub struct DisplayRegion<'a> { - region: &'a Region, - function: Option, - imports: &'a ModuleImportInfo, - indent: usize, -} -impl<'a> fmt::Display for DisplayRegion<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use miden_hir::DisplayMasmBlock; - - write!( - f, - "{}", - DisplayMasmBlock::new( - self.function, - Some(self.imports), - &self.region.blocks, - self.region.body, - self.indent - ) - ) - } -} - -/// Import code from a [miden_assembly::ast::CodeBody] into the specified [Block] in `region`. -fn import_code_body( - region: &mut Region, - current_block_id: BlockId, - code: &ast::CodeBody, - locals: &[FunctionIdent], - imported: &ast::ModuleImports, -) { - for node in code.nodes() { - match node { - ast::Node::Instruction(ix) => { - let current_block = region.block_mut(current_block_id); - let mut ops = Op::from_masm(ix.clone(), locals, imported); - current_block.append(&mut ops); - } - ast::Node::IfElse { - ref true_case, - ref false_case, - } => { - let then_blk = region.create_block(); - let else_blk = region.create_block(); - import_code_body(region, then_blk, true_case, locals, imported); - import_code_body(region, else_blk, false_case, locals, imported); - region - .block_mut(current_block_id) - .push(Op::If(then_blk, else_blk)); - } - ast::Node::Repeat { times, ref body } => { - let body_blk = region.create_block(); - import_code_body(region, body_blk, body, locals, imported); - region.block_mut(current_block_id).push(Op::Repeat( - (*times).try_into().expect("too many repetitions"), - body_blk, - )); - } - ast::Node::While { ref body } => { - let body_blk = region.create_block(); - import_code_body(region, body_blk, body, locals, imported); - region.block_mut(current_block_id).push(Op::While(body_blk)); - } - } - } -} - -/// Emit a [miden_assembly::ast::CodeBlock] by recursively visiting a tree of blocks -/// starting with `block_id`, using the provided imports and local/external procedure maps. -fn emit_block( - block_id: BlockId, - blocks: &PrimaryMap, - codemap: &miden_diagnostics::CodeMap, - imports: &ModuleImportInfo, - local_ids: &FxHashMap, - proc_ids: &FxHashMap, -) -> ast::CodeBody { - 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 { - Op::If(then_blk, else_blk) => { - let true_case = emit_block(then_blk, blocks, codemap, imports, local_ids, proc_ids); - let false_case = - emit_block(else_blk, blocks, codemap, imports, local_ids, proc_ids); - ops.push(ast::Node::IfElse { - true_case, - false_case, - }); - } - Op::While(blk) => { - let body = emit_block(blk, blocks, codemap, imports, local_ids, proc_ids); - ops.push(ast::Node::While { body }); - } - Op::Repeat(n, blk) => { - let body = emit_block(blk, blocks, codemap, imports, local_ids, proc_ids); - ops.push(ast::Node::Repeat { - times: n as u32, - body, - }); - } - op => { - ops.extend(op.into_node(codemap, imports, local_ids, proc_ids)); - } - } - } - - ast::CodeBody::new(ops) -} diff --git a/codegen/masm/src/tests.rs b/codegen/masm/src/tests.rs deleted file mode 100644 index ac9a6cc0e..000000000 --- a/codegen/masm/src/tests.rs +++ /dev/null @@ -1,804 +0,0 @@ -use miden_hir::{ - self, - pass::{AnalysisManager, ConversionPass}, - testing::{self, TestContext}, - AbiParam, Felt, FieldElement, FunctionIdent, Immediate, InstBuilder, OperandStack, - ProgramBuilder, Signature, SourceSpan, Stack, StarkField, Type, -}; -use std::sync::Arc; - -use proptest::prelude::*; - -use super::*; - -#[cfg(test)] -#[allow(unused_macros)] -macro_rules! assert_masm_output { - ($lhs:expr, $rhs:expr) => {{ - let lhs = $lhs; - let rhs = $rhs; - if lhs != rhs { - panic!( - r#" -assertion failed: `(left matches right)` -left: `{}`, -right: `{}`"#, - lhs, rhs - ); - } - }}; -} - -#[derive(Default)] -struct TestByEmulationHarness { - context: TestContext, - emulator: Emulator, -} -impl TestByEmulationHarness { - #[allow(unused)] - pub fn with_emulator_config(memory_size: usize, hp: usize, lp: usize) -> Self { - let mut harness = Self { - context: TestContext::default(), - emulator: Emulator::new( - memory_size.try_into().expect("invalid memory size"), - hp.try_into().expect("invalid address"), - lp.try_into().expect("invalid address"), - ), - }; - harness.set_cycle_budget(2000); - harness - } - - pub fn codegen( - &self, - program: &hir::Program, - function: &mut hir::Function, - ) -> CompilerResult> { - use miden_hir::{ - pass::{Analysis, RewritePass}, - ProgramAnalysisKey, - }; - use miden_hir_analysis as analysis; - use miden_hir_transform as transform; - - // Analyze function - let mut analyses = AnalysisManager::new(); - - // Register program-wide analyses - let global_analysis = analysis::GlobalVariableAnalysis::::analyze( - program, - &mut analyses, - &self.context.session, - )?; - analyses.insert(ProgramAnalysisKey, global_analysis); - - // Apply pre-codegen transformations - let mut rewrites = transform::SplitCriticalEdges - .chain(transform::Treeify) - .chain(transform::InlineBlocks); - rewrites.apply(function, &mut analyses, &self.context.session)?; - - println!("{}", function); - - // Run stackification - let mut convert_to_masm = ConvertHirToMasm::<&hir::Function>::default(); - 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) { - self.emulator.set_max_cycles(budget); - } - - #[allow(unused)] - pub fn set_breakpoint(&mut self, bp: Breakpoint) { - self.emulator.set_breakpoint(bp); - } - - #[allow(unused)] - pub fn clear_breakpoint(&mut self, bp: Breakpoint) { - self.emulator.clear_breakpoint(bp); - } - - #[allow(unused)] - pub fn resume_until_break(&mut self) { - match self.emulator.resume() { - Ok(_) | Err(EmulationError::BreakpointHit(_)) => (), - Err(other) => panic!("unexpected emulation error: {other}"), - } - } - - #[allow(unused)] - pub fn resume_until_break_with_info(&mut self) { - match self.emulator.resume() { - Ok(_) | Err(EmulationError::BreakpointHit(_)) => { - dbg!(self.emulator.info()); - } - Err(other) => panic!("unexpected emulation error: {other}"), - } - } - - pub fn malloc(&mut self, size: usize) -> u32 { - self.emulator.malloc(size) - } - - #[inline(always)] - pub fn store(&mut self, addr: usize, value: Felt) { - self.emulator.store(addr, value); - } - - pub fn execute_module( - &mut self, - module: Arc, - args: &[Felt], - ) -> Result, EmulationError> { - let entrypoint = module.entrypoint().expect("cannot execute a library"); - self.emulator - .load_module(module) - .expect("failed to load module"); - self.emulator.invoke(entrypoint, args) - } - - pub fn execute_program( - &mut self, - program: Arc, - args: &[Felt], - ) -> Result, EmulationError> { - self.emulator - .load_program(program) - .expect("failed to load program"); - { - let stack = self.emulator.stack_mut(); - for arg in args.iter().copied().rev() { - stack.push(arg); - } - } - self.emulator.start() - } - - #[allow(unused)] - pub fn execute_program_with_entry( - &mut self, - program: Arc, - entrypoint: FunctionIdent, - args: &[Felt], - ) -> Result, EmulationError> { - self.emulator - .load_program(program) - .expect("failed to load program"); - self.emulator.invoke(entrypoint, args) - } - - #[inline] - pub fn invoke( - &mut self, - entrypoint: FunctionIdent, - args: &[Felt], - ) -> Result, EmulationError> { - self.emulator.invoke(entrypoint, args) - } - - #[inline] - pub fn enter(&mut self, entrypoint: FunctionIdent, args: &[Felt]) { - match self.emulator.enter(entrypoint, args) { - Ok(_) | Err(EmulationError::BreakpointHit(_)) => { - dbg!(self.emulator.info()); - } - Err(other) => panic!("unexpected emulation error: {other}"), - } - } - - pub fn step_with_info(&mut self) { - match self.emulator.step() { - Ok(_) | Err(EmulationError::BreakpointHit(_)) => { - dbg!(self.emulator.info()); - } - Err(other) => panic!("unexpected emulation error: {other}"), - } - } - - #[inline] - pub fn step(&mut self) -> Result { - self.emulator.step() - } - - #[inline] - pub fn step_over(&mut self) -> Result { - self.emulator.step_over() - } -} - -#[test] -fn issue56() { - let harness = TestByEmulationHarness::default(); - - let mut builder = ProgramBuilder::new(&harness.context.session.diagnostics); - let mut mb = builder.module("test"); - testing::issue56(mb.as_mut(), &harness.context); - mb.build().unwrap(); - - let program = builder - .with_entrypoint("test::entrypoint".parse().unwrap()) - .link() - .unwrap(); - - let mut compiler = MasmCompiler::new(&harness.context.session); - compiler.compile(program).expect("compilation failed"); -} - -/// Test the emulator on the fibonacci function -#[test] -fn fib_emulator() { - let mut harness = TestByEmulationHarness::default(); - - // Build a simple program - let mut builder = ProgramBuilder::new(&harness.context.session.diagnostics); - - // Build test module with fib function - let mut mb = builder.module("test"); - testing::fib1(mb.as_mut(), &harness.context); - mb.build() - .expect("unexpected error constructing test module"); - - // Link the program - let program = builder - .with_entrypoint("test::fib".parse().unwrap()) - .link() - .expect("failed to link program"); - - let mut compiler = MasmCompiler::new(&harness.context.session); - let program = compiler.compile(program).expect("compilation failed"); - - println!("{}", program.get("test").unwrap()); - - // Test it via the emulator - let n = Felt::new(10); - let mut stack = harness - .execute_program(program.freeze(), &[n]) - .expect("execution failed"); - assert_eq!(stack.len(), 1); - assert_eq!(stack.pop().map(|e| e.as_int()), Some(55)); -} - -/// Test the code generator on a very simple program with a conditional as a sanity check -#[test] -fn codegen_fundamental_if() { - let mut harness = TestByEmulationHarness::default(); - - // Build a simple program - let mut builder = ProgramBuilder::new(&harness.context.session.diagnostics); - - // Build test module with function that adds two numbers if the - // first number is odd, and multiplies them if the first number is even - let mut mb = builder.module("test"); - let id = { - let mut fb = mb - .function( - "add_odd_mul_even", - Signature::new( - [AbiParam::new(Type::U32), AbiParam::new(Type::U32)], - [AbiParam::new(Type::U32)], - ), - ) - .expect("unexpected symbol conflict"); - let entry = fb.current_block(); - let (a, b) = { - let args = fb.block_params(entry); - (args[0], args[1]) - }; - let is_odd_blk = fb.create_block(); - let is_even_blk = fb.create_block(); - let is_odd = fb.ins().is_odd(a, SourceSpan::UNKNOWN); - fb.ins().cond_br( - is_odd, - is_odd_blk, - &[], - is_even_blk, - &[], - SourceSpan::UNKNOWN, - ); - fb.switch_to_block(is_odd_blk); - let c = fb.ins().add_checked(a, b, SourceSpan::UNKNOWN); - fb.ins().ret(Some(c), SourceSpan::UNKNOWN); - fb.switch_to_block(is_even_blk); - let d = fb.ins().mul_checked(a, b, SourceSpan::UNKNOWN); - fb.ins().ret(Some(d), SourceSpan::UNKNOWN); - fb.build().expect("unexpected error building function") - }; - - mb.build() - .expect("unexpected error constructing test module"); - - // Link the program - 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 a = Felt::new(3); - let b = Felt::new(4); - - let mut stack = harness - .execute_program(program.freeze(), &[a, b]) - .expect("execution failed"); - assert_eq!(stack.len(), 1); - assert_eq!(stack.pop().map(|e| e.as_int()), Some(12)); -} - -/// Test the code generator on a very simple program with a loop as a sanity check -#[test] -fn codegen_fundamental_loops() { - let mut harness = TestByEmulationHarness::default(); - - // Build a simple program - let mut builder = ProgramBuilder::new(&harness.context.session.diagnostics); - - // Build test module with function that increments a number until a - // given iteration count is reached - let mut mb = builder.module("test"); - let id = { - let mut fb = mb - .function( - "incr_until", - Signature::new( - [AbiParam::new(Type::U32), AbiParam::new(Type::U32)], - [AbiParam::new(Type::U32)], - ), - ) - .expect("unexpected symbol conflict"); - let entry = fb.current_block(); - let (a, n) = { - let args = fb.block_params(entry); - (args[0], args[1]) - }; - let loop_header_blk = fb.create_block(); - let a1 = fb.append_block_param(loop_header_blk, Type::U32, SourceSpan::UNKNOWN); - let n1 = fb.append_block_param(loop_header_blk, Type::U32, SourceSpan::UNKNOWN); - let loop_body_blk = fb.create_block(); - let loop_exit_blk = fb.create_block(); - let result0 = fb.append_block_param(loop_exit_blk, Type::U32, SourceSpan::UNKNOWN); - fb.ins().br(loop_header_blk, &[a, n], SourceSpan::UNKNOWN); - - fb.switch_to_block(loop_header_blk); - let is_zero = fb.ins().eq_imm(n1, Immediate::U32(0), SourceSpan::UNKNOWN); - fb.ins().cond_br( - is_zero, - loop_exit_blk, - &[a1], - loop_body_blk, - &[], - SourceSpan::UNKNOWN, - ); - - fb.switch_to_block(loop_body_blk); - let a2 = fb.ins().incr_checked(a1, SourceSpan::UNKNOWN); - let n2 = fb - .ins() - .sub_imm_checked(n1, Immediate::U32(1), SourceSpan::UNKNOWN); - fb.ins().br(loop_header_blk, &[a2, n2], SourceSpan::UNKNOWN); - - fb.switch_to_block(loop_exit_blk); - fb.ins().ret(Some(result0), SourceSpan::UNKNOWN); - - fb.build().expect("unexpected error building function") - }; - - mb.build() - .expect("unexpected error constructing test module"); - - // Link the program - 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 a = Felt::new(3); - let n = Felt::new(4); - - let mut stack = harness - .execute_program(program.freeze(), &[a, n]) - .expect("execution failed"); - assert_eq!(stack.len(), 1); - assert_eq!(stack.pop().map(|e| e.as_int()), Some(7)); -} - -/// Test the code generator on a simple program containing [testing::sum_matrix]. -#[test] -fn codegen_sum_matrix() { - let mut harness = TestByEmulationHarness::default(); - - // Build a simple program - let mut builder = ProgramBuilder::new(&harness.context.session.diagnostics); - - // Build test module with fib function - let mut mb = builder.module("test"); - testing::sum_matrix(mb.as_mut(), &harness.context); - mb.build() - .expect("unexpected error constructing test module"); - - // Link the program - let program = builder - .with_entrypoint("test::sum_matrix".parse().unwrap()) - .link() - .expect("failed to link program"); - - // Compile - let mut compiler = MasmCompiler::new(&harness.context.session); - let program = compiler.compile(program).expect("compilation failed"); - - println!("{}", program.get("test").unwrap()); - - // Prep emulator - let addr = harness.malloc(core::mem::size_of::() * 3 * 3); - let ptr = Felt::new(addr as u64); - let rows = Felt::new(3); - let cols = Felt::new(3); - - // [1, 0, 1, - // 0, 1, 0, - // 1, 1, 1] == 6 - let addr = addr as usize; - harness.store(addr, Felt::ONE); - harness.store(addr + 4, Felt::ZERO); - harness.store(addr + 8, Felt::ONE); - harness.store(addr + 12, Felt::ZERO); - harness.store(addr + 16, Felt::ONE); - harness.store(addr + 20, Felt::ZERO); - harness.store(addr + 24, Felt::ONE); - harness.store(addr + 28, Felt::ONE); - harness.store(addr + 32, Felt::ONE); - - // Execute test::sum_matrix - let mut stack = harness - .execute_program(program.freeze(), &[ptr, rows, cols]) - .expect("execution failed"); - assert_eq!(stack.len(), 1); - assert_eq!(stack.pop().map(|e| e.as_int()), Some(6)); -} - -#[test] -#[should_panic(expected = "assertion failed: expected false, got true")] -fn i32_checked_neg() { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let min = Felt::new(i32::MIN as u32 as u64); - - let neg = "intrinsics::i32::checked_neg".parse().unwrap(); - // i32::MIN - harness.invoke(neg, &[min]).expect("execution failed"); -} - -proptest! { - #![proptest_config(ProptestConfig { cases: 1000, failure_persistence: None, ..Default::default() })] - - #[test] - fn i32_icmp(a: i32, b: i32) { - use core::cmp::Ordering; - - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsics module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - // NOTE: arguments are passed in reverse, i.e. [b, a] not [a, b] - let icmp = "intrinsics::i32::icmp".parse().unwrap(); - let is_gt = "intrinsics::i32::is_gt".parse().unwrap(); - let is_lt = "intrinsics::i32::is_lt".parse().unwrap(); - - let a_felt = Felt::new(a as u32 as u64); - let b_felt = Felt::new(b as u32 as u64); - let mut stack = harness.invoke(icmp, &[b_felt, a_felt]).expect("execution failed"); - harness.emulator.stop(); - prop_assert_eq!(stack.len(), 1); - - const NEG_ONE_U64: u64 = -1i32 as u32 as u64; - let result = match stack.pop().unwrap().as_int() { - NEG_ONE_U64 => Ordering::Less, - 0 => Ordering::Equal, - 1 => Ordering::Greater, - other => panic!("invalid comparison result, expected -1, 0, or 1 but got {other}"), - }; - - prop_assert_eq!(result, a.cmp(&b)); - - let mut stack = harness.invoke(is_gt, &[b_felt, a_felt]).expect("execution failed"); - harness.emulator.stop(); - prop_assert_eq!(stack.len(), 1); - - let result = match stack.pop().unwrap().as_int() { - 0 => false, - 1 => true, - other => panic!("invalid boolean result, expected 0 or 1, got {other}"), - }; - - prop_assert_eq!(result, a.cmp(&b).is_gt()); - - let mut stack = harness.invoke(is_lt, &[b_felt, a_felt]).expect("execution failed"); - harness.emulator.stop(); - prop_assert_eq!(stack.len(), 1); - - let result = match stack.pop().unwrap().as_int() { - 0 => false, - 1 => true, - other => panic!("invalid boolean result, expected 0 or 1, got {other}"), - }; - - prop_assert_eq!(result, a.cmp(&b).is_lt()); - } - - #[test] - fn i32_is_signed(a: i32) { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u32 as u64); - let is_signed = "intrinsics::i32::is_signed".parse().unwrap(); - let mut stack = harness.invoke(is_signed, &[a_felt]).expect("execution failed"); - prop_assert_eq!(stack.len(), 1); - let result = stack.pop().unwrap().as_int(); - - prop_assert_eq!(result, a.is_negative() as u64); - } - - #[test] - fn i32_overflowing_add(a: i32, b: i32) { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u32 as u64); - let b_felt = Felt::new(b as u32 as u64); - // NOTE: arguments are passed in reverse, i.e. [b, a] not [a, b] - let add = "intrinsics::i32::overflowing_add".parse().unwrap(); - let mut stack = harness - .invoke(add, &[b_felt, a_felt]) - .expect("execution failed"); - prop_assert_eq!(stack.len(), 2); - - let overflowed = stack.pop().unwrap() == Felt::ONE; - let raw_result = stack.pop().unwrap().as_int(); - let result = u32::try_from(raw_result).map_err(|_| raw_result).map(|res| res as i32); - let (expected_result, expected_overflow) = a.overflowing_add(b); - - prop_assert_eq!((result, overflowed), (Ok(expected_result), expected_overflow)); - } - - #[test] - fn i32_overflowing_sub(a: i32, b: i32) { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u32 as u64); - let b_felt = Felt::new(b as u32 as u64); - // NOTE: arguments are passed in reverse, i.e. [b, a] not [a, b] - let sub = "intrinsics::i32::overflowing_sub".parse().unwrap(); - let mut stack = harness - .invoke(sub, &[b_felt, a_felt]) - .expect("execution failed"); - prop_assert_eq!(stack.len(), 2); - - let overflowed = stack.pop().unwrap() == Felt::ONE; - let raw_result = stack.pop().unwrap().as_int(); - let result = u32::try_from(raw_result).map_err(|_| raw_result).map(|res| res as i32); - let (expected_result, expected_overflow) = a.overflowing_sub(b); - - prop_assert_eq!((result, overflowed), (Ok(expected_result), expected_overflow)); - } - - #[test] - fn i32_overflowing_mul(a: i32, b: i32) { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u32 as u64); - let b_felt = Felt::new(b as u32 as u64); - // NOTE: arguments are passed in reverse, i.e. [b, a] not [a, b] - let mul = "intrinsics::i32::overflowing_mul".parse().unwrap(); - let mut stack = harness - .invoke(mul, &[b_felt, a_felt]) - .expect("execution failed"); - prop_assert_eq!(stack.len(), 2); - - let overflowed = stack.pop().unwrap() == Felt::ONE; - let raw_result = stack.pop().unwrap().as_int(); - let result = u32::try_from(raw_result).map_err(|_| raw_result).map(|res| res as i32); - let (expected_result, expected_overflow) = a.overflowing_mul(b); - - prop_assert_eq!((result, overflowed), (Ok(expected_result), expected_overflow)); - } - - #[test] - fn i32_unchecked_neg(a: i32) { - prop_assume!(a != i32::MIN, "unchecked_neg is meaningless for i32::MIN"); - - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u32 as u64); - let neg = "intrinsics::i32::unchecked_neg".parse().unwrap(); - let mut stack = harness.invoke(neg, &[a_felt]).expect("execution failed"); - - prop_assert_eq!(stack.len(), 1); - let raw_result = stack.pop().unwrap().as_int(); - let result = u32::try_from(raw_result).map_err(|_| raw_result).map(|res| res as i32); - prop_assert_eq!(result, Ok(-a)); - } - - #[test] - fn i32_checked_div(a: i32, b: core::num::NonZeroI32) { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u32 as u64); - let b_felt = Felt::new(b.get() as u32 as u64); - let div = "intrinsics::i32::checked_div".parse().unwrap(); - let mut stack = harness.invoke(div, &[b_felt, a_felt]).expect("execution failed"); - - prop_assert_eq!(stack.len(), 1); - let raw_result = stack.pop().unwrap().as_int(); - let result = u32::try_from(raw_result).map_err(|_| raw_result).map(|res| res as i32); - prop_assert_eq!(result, Ok(a / b.get())); - } - - #[test] - fn i32_pow2(a in 0u32..30u32) { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u64); - let pow2 = "intrinsics::i32::pow2".parse().unwrap(); - let mut stack = harness.invoke(pow2, &[a_felt]).expect("execution failed"); - - prop_assert_eq!(stack.len(), 1); - let raw_result = stack.pop().unwrap().as_int(); - let result = u32::try_from(raw_result).map_err(|_| raw_result).map(|res| res as i32); - prop_assert_eq!(result, Ok(2i32.pow(a))); - } - - #[test] - fn i32_ipow(a: i32, b in 0u32..30u32) { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u32 as u64); - let b_felt = Felt::new(b as u64); - let ipow = "intrinsics::i32::ipow".parse().unwrap(); - let mut stack = harness.invoke(ipow, &[b_felt, a_felt]).expect("execution failed"); - - prop_assert_eq!(stack.len(), 1); - let raw_result = stack.pop().unwrap().as_int(); - let result = u32::try_from(raw_result).map_err(|_| raw_result).map(|res| res as i32); - prop_assert_eq!(result, Ok(a.wrapping_pow(b))); - } - - #[test] - fn i32_checked_shr(a: i32, b in 0i32..32i32) { - let mut harness = TestByEmulationHarness::default(); - - harness - .emulator - .load_module( - Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) - .expect("undefined intrinsic module"), - ) - .freeze(), - ) - .expect("failed to load intrinsics::i32"); - - let a_felt = Felt::new(a as u32 as u64); - let b_felt = Felt::new(b as u32 as u64); - let shr = "intrinsics::i32::checked_shr".parse().unwrap(); - let mut stack = harness.invoke(shr, &[b_felt, a_felt]).expect("execution failed"); - - prop_assert_eq!(stack.len(), 1); - let raw_result = stack.pop().unwrap().as_int(); - let result = u32::try_from(raw_result).map_err(|_| raw_result).map(|res| res as i32); - prop_assert_eq!(result, Ok(a >> b)); - } -} diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 7585238ef..000000000 --- a/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index ee74410d1..000000000 --- a/docs/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Miden Compiler Docs - -This directory contains the sources for the [Miden Compiler Documentation](https://0xpolygonmiden.github.io/miden-ir/). - -All doc files are written in Markdown, and are converted into an online book using the [mdBook](https://github.com/rust-lang/mdBook) utility. - -# License - -MIT diff --git a/docs/book.toml b/docs/book.toml deleted file mode 100644 index aaf632a65..000000000 --- a/docs/book.toml +++ /dev/null @@ -1,9 +0,0 @@ -[book] -authors = [] -language = "en" -multilingual = false -src = "src" -title = "Miden Compiler Documentation" - -[output.html] -git-repository-url = "https://github.com/0xPolygonMiden/compiler/" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md deleted file mode 100644 index 38b7741dd..000000000 --- a/docs/src/SUMMARY.md +++ /dev/null @@ -1,37 +0,0 @@ -# Reference - -[Getting Started](getting_started.md) - ---- - -# Usage - -- [As an Executable](usage/midenc.md) -- [As a Library]() -- [As a Cargo extension]() - -# Guides - -- [Rust To WebAssembly](guides/rust_to_wasm.md) -- [WebAssembly To Miden Assembly](guides/wasm_to_masm.md) - -# Usage - -- [midenc]() - - [compile]() - - [exec]() - - [run]() - -- [cargo miden]() - -# Architecture - -- [Overview](design/overview.md) -- [Frontends]() -- [HIR]() -- [Code Generation]() -- [Testing]() - ---- - -[Calling Conventions](appendix/calling_conventions.md) diff --git a/docs/src/appendix/calling_conventions.md b/docs/src/appendix/calling_conventions.md deleted file mode 100644 index 8bf8b9fc3..000000000 --- a/docs/src/appendix/calling_conventions.md +++ /dev/null @@ -1,289 +0,0 @@ -# Calling Conventions - -This document describes the various calling conventions recognized/handled by the compiler, -including a specification for the interaction with the IR type system. - -There are four calling conventions represented in the compiler: - -- `C` aka `SystemV`, which corresponds to the C ABI commonly used for C foreign-function interfaces (FFI). - We specifically use the System V ABI because it is well understood, documented, and straightforward. -- `Fast`, this convention allows the compiler to follow either the `C` calling convention, or modify it - as it sees fit on a function-by-function basis. This convention provides no guarantees about how a - callee will expect arguments to be passed, so should not be used for functions which are expected to - have a stable, predicatble interface. This is a good choice for local functions, or functions which are - only used within an executable/library and are not part of the public interface. -- `Kernel`, this is a special calling convention that is used when defining kernel modules in the IR. - Functions which are part of the kernel's public API are required to use this convention, and it is not - possible to call a function via `syscall` if the callee is not defined with this convention. Because of - the semantics of `syscall`, this convention is highly restrictive. In particular, it is not permitted to - pass pointer arguments, or aggregates containing pointers, as `syscall` involves a context switch, and - thus memory in the the caller is not accessible to the callee, and vice versa. -- `Contract`, this is a special calling convention that is used when defining smart contract functions, i.e. - functions that can be `call`'d. The compiler will not permit you to `call` a function if the callee is not - defined with this convention, and functions with this convention cannot be called via `exec`. Like `syscall`, - the `call` instruction involves a context switch, however, unlike the `Kernel` convention, the `Contract` - convention is allowed to have types in its signature that are/contain pointers, with certain caveats around - those pointers. - - -All four conventions above are based on the System V C ABI, tailored to the Miden VM. The only exception is -`Fast`, which may modify the ABI arbitrarily as it sees fit, and makes no guarantees about what modifications, -if any, it will make. - -# Data Representation - -The following is a description of how the IR type system is represented in the `C` calling convention. Later, -a description of how the other conventions extend/restrict/modify this representation will be provided. - -## Scalars - -General type | C Type | IR Type | `sizeof` | Alignment (bytes) | Miden Type --|-|-|-|-|- - Integer | `_Bool`/`bool` | `I1` | 1 | 1 | u32 - Integer | `char`, `signed char` | `I8` | 1 | 1 | i32[^1] - Integer | `unsigned char` | `U8` | 1 | 1 | u32 - Integer | `short` / `signed short` | `I16` | 2 | 2 | i32[^1] - Integer | `unsigned short` | `U16` | 2 | 2 | u32 - Integer | `int` / `signed int` / `enum` | `I32` | 4 | 4 | i32[^1][^8] - Integer | `unsigned int` | `U32` | 4 | 4 | u32 - Integer | `long` / `signed long` | `I32` | 4 | 4 | i32[^1] - Integer | `unsigned long` / `size_t` | `U32` | 4 | 4 | u32 - Integer | `long long` / `signed long long` | `I64` | 8 | 8 | i64[^2] - Integer | `unsigned long long` | `U64` | 8 | 8 | u64[^3] - Pointer | *`any-type *`* / *`any-type (*)()`* | `Ptr(_)` | 4 | 4 | u32[^6][^7] - Floating point | `float` | `F32` | 4 | 4 | u32[^4] - Floating point | `double` | `F64` | 8 | 8 | u64[^4] - Floating point | `long double` | 16 | 16 | (none)[^5] - -[^1]: i32 is not a native Miden type, but is implemented using compiler intrinsics on top of the native u32 type -[^2]: i64 is not a native Miden type, but is implemented using compiler intrinsics on top of the stdlib u64 type -[^3]: u64 is not a native Miden type, but is implemented in software using two 32-bit limbs (i.e. a pair of field elements) -[^4]: floating-point types are not currently supported, but will be implemented using compiler intrinsics -[^5]: `long double` values correspond to 128-bit IEEE-754 quad-precision binary128 values. These are not currently -supported, and we have no plans to support them in the near term. Should we ever provide such support, we will do -so using compiler intrinsics. -[^6]: A null pointer (for all types) always has the value zero. -[^7]: Miden's linear memory is word-addressable, not byte-addressable. The `Ptr` type has an `AddressSpace` parameter, -that by default is set to the byte-addressable address space. The compiler translates values of `Ptr` type that are in -this address space, into the Miden-native, word-addressable address space during codegen of load/store operations. See -the section on the memory model below for more details. -[^8]: An `enum` is `i32` if all members of the enumeration can be represented by an `int`/`unsigned int`, otherwise it -uses i64. - -NOTE: The compiler does not support scalars larger than one word (128 bits) at this time. As a result, anything that is -larger than that must be allocated in linear memory, or in an automatic allocation (function-local memory), and passed -around by reference. - -The native scalar type for the Miden VM is a "field element", specifically a 64-bit value representing an integer -in the "Goldilocks" field, i.e. `0..(2^64-2^32+1)`. A number of instructions in the VM operate on field elements directly. -However, the native integral/pointer type, i.e. a "machine word", is actually `u32`. This is because a field element -can fully represent 32-bit integers, but not the full 64-bit integer range. Values of `u32` type are valid field element -values, and can be used anywhere that a field element is expected (barring other constraints). - -Miden also has the notion of a "word", not to be confused with a "machine word" (by which we mean the native integral -type used to represent pointers), which corresponds to a set of 4 field elements. Words are commonly used in Miden, -particularly to represent hashes, and a number of VM instructions operate on word-sized operands. As an aside, 128-bit -integer values are represented using a word, or two 64-bit limbs (each limb consisting of two 32-bit limbs). - -All integral types mentioned above, barring field elements, use two's complement encoding. Unsigned integral types -make use of the sign bit to change the value range (i.e. 0..2^32-1, rather than -2^31..2^31-1), but the encoding follows -two's complement rules. - -The Miden VM only has native support for field elements, words, and `u32`; all other types are implemented in software -using intrinsics. - -## Aggregates and Unions - -Structures and unions assume the alignment of their most strictly aligned component. Each member is assigned to the -lowest available offset with the appropriate alignment. The size of any object is always a multiple of the object's alignment. -An array uses the same alignment as its elements. Structure and union objects can require padding to meet size and alignment -constraints. The contents of any padding is undefined. - -## Memory Model - -Interacting with memory in Miden is quite similar to WebAssembly in some ways: - -* The address space is linear, with addresses starting at zero, and ranging up to 2^32-1 -* There is no memory protection per se, you either have full read/write access, or no access to a specific memory context -* How memory is used is completely up the program being executed - -This is where it begins to differ though, and takes on qualities unique to Miden (in part, or whole): - -* Certain regions of the address space are "reserved" for special uses, improper use of those regions may result in -undefined behavior. -* Miden has different types of function call instructions: `call` vs `syscall` vs `exec`. The first two -perform a context switch when transferring control to the callee, and the callee has no access to the -caller's memory (and the caller has no access to the callee's memory). As a result, references to memory -cannot be passed from caller to callee in arguments, nor can they be returned from the callee to the caller. -* Most significant of all though, is that Miden does not have byte-addressable memory, it is instead word-addressable, -i.e. every address refers to a full word. -* It is not possible to load a specific field element from a word in memory, unless it happens to be the first element -of the word. Instead, one must load the full word, and drop the elements you don't need. - -This presents some complications, particularly: - -* Most languages assume a byte-oriented memory model, which is not trivially mapped to a word-oriented model -* Simple things, such as taking the address of a field in a struct, and then dereferencing it, cannot be directly -represented in Miden using native pointer arithmetic and `load` instruction. Operations like this must be translated -into instruction sequences that load whole words from memory, extract the data needed, and discard the unused bits. -This makes the choice of where in memory to store something much more important than byte-addressable memory, as -loads of values which are not aligned to element or word boundaries can be quite inefficient in some cases. - -The compiler solves this by providing a byte-addressable IR, and internally translating operations in the IR to the equivalent -sequence of Miden instructions needed to emulate that operation. This translation is done during code generation, and uses -the following semantics to determine how a particular operation gets lowered: - -* A byte-addressable pointer can be emulated in Miden's word-addressable environment using three pieces of information: - - The address of the word containing the first byte of the value, this is a "native" Miden address value - - The index of the field element within that word containing the first byte of the value - - The offset (in bytes) from the start of the 4 byte chunk represented by the selected element, corresponding - to the first byte of the value. Since the chunk is represented as a u32 value, the offset is relative to the - most-significant bit (i.e. the byte with the lowest address is found in bits 55-63, since Miden integers are little-endian) -* This relies on us treating Miden's linear memory as an array of 16-byte chunks of raw memory (each word is 4 field elements, -each element represents a 4-byte chunk). In short, much like translating a virtual memory address to a physical one, we must -translate byte-addressable "virtual" pointers to "real" Miden pointers with enough metadata to be able to extract the data we're -trying to load (or encode the data we're trying to store). - -Because we're essentially emulating byte-addressable memory on word-addressable memory, loads/stores can range from simple and -straightforward, to expensive and complicated, depending on the size and alignment of the value type. The process goes as follows: - -* If the value type is word-aligned, it can be loaded/stored in as little as a single instruction depending on the size of the type -* Likewise if the value type is element-aligned, and the address is word-aligned -* Element-aligned values require some extra instructions to load a full word and drop the unused elements (or in the case of stores, -loading the full word and replacing the element being stored) -* Loads/stores of types with sub-element alignment depend on the alignment of the pointer itself. Element or word-aligned addresses -are still quite efficient to load/store from, but if the first byte of the value occurs in the middle of an element, then the bytes -of that value must be shifted into place (or unused bytes masked out). If the value crosses an element boundary, then the bytes in -both elements must be isolated and shifted into position such that they can be bitwise-OR'd together to obtain the aligned value on -the operand stack. If a value crosses a word boundary, then elements from both words must be loaded, irrelevant ones discarded, the -relevant bytes isolated and shifted into position so that the resulting operand on the stack is aligned and laid out correctly. -* Stores are further complicated by the need to preserve memory that is not being explicitly written to, so values that do not overwrite -a full word or element, require combining bytes from the operand being stored and what currently resides in memory. - -The worst case scenario for an unaligned load or store involves a word-sized type starting somewhere in the last element of the first -word. This will require loading elements from three consecutive words, plus a lot of shuffling bits around to get the final, aligned -word-sized value on the operand stack. Luckily, such operations should be quite rare, as by default all word-sized scalar types are -word-aligned or element-aligned, so an unaligned load or store would require either a packed struct, or a type such as an array of -bytes starting at some arbitrary address. In practice, most loads/stores are likely to be element-aligned, so most overhead from -emulation will come from values which cross an element or word boundary. - -# Function Calls - -This section describes the conventions followed when executing a function call via `exec`, including how arguments are passed on the -operand stack, stack frames, etc. Later, we'll cover the differences when executing calls via `call` or `syscall`. - -## Locals and the stack frame - -Miden does not have registers in the style of hardware architectures. Instead it has an operand stack, on which an arbitrary number of -operands may be stored, and local variables. In both cases - an operand on the operand stack, or a single local variable - the value -type is nominally a field element, but it is easier to reason about them as untyped element-sized values. The operand stack is used -for function arguments, return values, temporary variables, and scratch space. Local variables are not always used, but are typically -used to hold multiply-used values which you don't want to keep on the operand stack, function-scoped automatic allocations (i.e. `alloca`), -and other such uses. - -Miden does not have a stack frame per se. When you call a procedure in Miden Assembly, any local variables declared by that procedure -are allocated space in a reserved region of linear memory in a single consecutive chunk. However, there is no stack or frame pointer, -and because Miden is a Harvard architecture machine, there are no return addresses. Instead, languages (such as C) which have the concept -of a stack frame with implications for the semantics of say, taking the address of a local variable, will need to emit code in function -prologues and epilogues to maintain a shadow stack in Miden's linear memory. If all you need is local variables, you can get away with -leaning on Miden's notion of local variables without implenting a shadow stack. - -Because there are no registers, the notion of callee-saved or caller-saved registers does not have a direct equivalent in Miden. However, -in its place, a somewhat equivalent set of rules defines the contract betweeen caller and callee in terms of the state of the operand stack, -those are described below in the section covering the operand stack. - -### The shadow stack - -Miden is a [Harvard](https://en.wikipedia.org/wiki/Harvard_architecture) architecture; as such, code and data are not in the same memory -space. More precisely, in Miden, code is only addressable via the hash of the MAST root of that code, which must correspond to code that -has been loaded into the VM. The hash of the MAST root of a function can be used to call that function both directly and indirectly, but -that is the only action you can take with it. Code can not be generated and called on the fly, and it is not stored anywhere that is -accessible to code that is currently executing. - -One consequence of this is that there are no return addresses or instruction pointers visible to executing code. The runtime call stack is -managed by the VM itself, and is not exposed to executing code in any way. This means that address-taken local C variables need to be on a -separate stack in linear memory (which we refer to as a "shadow stack"). Not all functions necessarily require a frame in the shadow stack, -as it cannot be used to perform unwinding, so only functions which have locals require a frame. - -The Miden VM actually provides some built-in support for stack frames when using Miden Assembly. Procedures which are declared with some -number of locals, will be automatically allocated sufficient space for those locals in a reserved region of linear memory when called. If -you use the `locaddr` instruction to get the actual address of a local, that address can be passed as an argument to callees (within the -constraints of the callee's calling convention). - -Languages with more elaborate requirements with regard to the stack will need to implement their own shadow stack, and emit code in function -prologues/epilogues to manage it. - -### The operand stack - -The Miden virtual machine is a stack machine, not a register machine. Rather than having a fixed set of registers that are used to -store and manipulate scalar values, the Miden VM has the operand stack, which can hold an arbitrary number of operands (where each -operand is a single field element), of which the first 16 can be directly manipulated using special stack instructions. The operand -stack is, as the name implies, a last-in/first-out data structure. - -The following are basic rules all conventions are expected to follow with regard to the operand stack: - -1. The state of the operand stack from the point of view of the caller should be preserved, with two exceptions: - - The callee is expected to consume all of its arguments, and the caller will expect those operands to be gone when control is returned to it - - If the callee signature declares a return value, the caller expects to see that on top of the stack when control is returned to it -2. No more than 16 elements of the operand stack may be used for passing arguments. If more than that is required to represent all of the arguments, -then one of the following must happen: - - Spill to stack frame: in this scenario, up to 15 elements of the operand stack are used for arguments, and the remaining element is used to hold - a pointer to a local variable in the caller's stack frame. That local variable is a struct whose fields are the spilled arguments, appearing in - the same order as they would be passed. The callee must use the pointer it is given to compute the effective address for each spilled argument - that it wishes to access. - - Spill to heap: this is basically identical to the approach above, except the memory is allocated from the global heap, rather than using memory - associated with the caller's stack frame. - - Spill to the advice provider: in this scenario, 12 elements of the stack are used for arguments, and the remaining 4 are used to hold a hash - which refers to the remaining arguments on the advice provider stack. The callee must arrange to fetch the spilled arguments from the advice - provider using that hash. - -### Function signatures - -Miden Abstract Syntax Trees (MASTs) do not have any notion of functions, and as such are not aware of parameters, return values, etc. For -this document, that's not a useful level of abstraction to examine. Even a step higher, Miden Assembly (MASM) has functions (procedures -in MASM parlance), but no function signature, i.e. given a MASM procedure, there is no way to know how many arguments it expects, how -many values it returns, let alone the types of arguments/return values. Instead, we're going to specify calling conventions in terms of -Miden IR, which has a fairly expressive type system more or less equivalent to that of LLVM, and how that translates to Miden primitives. - -Functions in Miden IR always have a signature, which specify the following: - -* The calling convention required to call the function -* The number and types of the function arguments -* The type of value, if any, returned by by the function, and whether it is returned by value or reference - -The following table relates IR types to how they are expected to be passed from the caller to the callee, and vice versa: - -Type | Parameter | Result | ---------------------------|---------------|----------| -scalar | direct | direct | -empty struct or union[^1] | ignored | ignored | -scalar struct or union[^2] | direct | direct | -other struct or union | indirect | indirect | -array | indirect | N/A | - -[^1]: Zero-sized types have no representation in memory, so they are ignored/skipped -[^2]: Any struct or union that recursively (including through nested structs, -unions, and arrays) contains just a single scalar value and is not specified to -have greater than natural alignment. - -The compiler will automatically generate code that follows these rules, but if emitting MASM from your own backend, it is necessary to do so manually. -For example, a function whose signature specifies that it returns a non-scalar struct by value, must actually be written such that it expects to receive -a pointer to memory allocated by the caller sufficient to hold the return value, as the first parameter of the function (i.e. the parameter is prepended -to the parameter list). When returning, the function must write the return value to that pointer, rather than returning it on the operand stack. In this -example, the return value is returned indirectly (by reference). - -A universal rule is that the arguments are passed in reverse order, i.e. the first argument in the parameter list of a function will be on top of the -operand stack. This is different than many Miden instructions which seemingly use the opposite convention, e.g. `add`, which expects the right-hand -operand on top of the stack, so `a + b` is represented like `push a, push b, add`. If we were to implement `add` as a function, it would instead be -`push b, push a, exec.add`. The rationale behind this is that, in general, the more frequently used arguments appear earlier in the parameter list, -and thus we want those closer to the top of the operand stack to reduce the amount of stack manipulation we need to do. - -Arguments/return values are laid out on the operand stack just like they would be as if you had just loaded it from memory, so all arguments are aligned, -but may span multiple operands on the operand stack as necessary based on the size of the type (i.e. a struct type that contains a `u32` and a `i1` -field would require two operands to represent). If the maximum number of operands allowed for the call is reached, any remaining arguments must be -spilled to the caller's stack frame, or to the advice provider. The former is used in the case of `exec`/`dynexec`, while the latter is used for `call` -and `syscall`, as caller memory is not accessible to the callee with those instructions. - -While ostensibly 16 elements is the maximum number of operands on the operand stack that can represent function arguments, due to the way `dynexec`/`dyncall` -work, it is actually limited to 12 elements, because at least 4 must be free to hold the hash of the function being indirectly called. - diff --git a/docs/src/assets/.gitkeep b/docs/src/assets/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/src/design/overview.md b/docs/src/design/overview.md deleted file mode 100644 index b7bc73b4f..000000000 --- a/docs/src/design/overview.md +++ /dev/null @@ -1,4 +0,0 @@ -# Compiler Architecture - -TODO - diff --git a/docs/src/getting_started.md b/docs/src/getting_started.md deleted file mode 100644 index d2a737027..000000000 --- a/docs/src/getting_started.md +++ /dev/null @@ -1,22 +0,0 @@ -# Getting Started - -This book attempts to provide a thorough reference for compiling to Miden Assembly -using one or more components of the Miden compiler suite. Which components you use, -and how you use them is likely to differ depending on the project, but we've tried -to provide good coverage regardless. - -There are a set of guides which are focused on documenting the workflows for specific -use cases that we wish to ensure are well supported, or have encountered so far, but -if you feel there is anything missing, feel free to open an issue and we will try to -address the missing docs as soon as possible! - -## Installation - -There are three ways you might use the Miden compiler: - - -1. As an executable (via `midenc`) -2. As a library (most likely via the `midenc-compile` and `miden-hir` crates) -3. As a Cargo extension (via `cargo miden`) - -Each of these is described in the following chapters, we hope you find this book useful! diff --git a/docs/src/guides/rust_to_wasm.md b/docs/src/guides/rust_to_wasm.md deleted file mode 100644 index 34006c1f9..000000000 --- a/docs/src/guides/rust_to_wasm.md +++ /dev/null @@ -1,182 +0,0 @@ -# Compiling Rust To WebAssembly - -This chapter will walk you through compiling a Rust crate to a WebAssembly (Wasm) module -in binary (i.e. `.wasm`) form. The Miden compiler has a frontend which can take such -modules and compile them on to Miden Assembly, which will be covered in the next chapter. - -## Setup - -First, let's set up a simple Rust project that contains an implementation of the Fibonacci -function (I know, its overdone, but we're trying to keep things as simple as possible to -make it easier to show the results at each step, so bear with me): - -Start by creating a new library crate: - - $ cargo new --lib wasm-fib && cd wasm-fib - - -To compile to WebAssembly, you must have the appropriate Rust toolchain installed, and we -will also need additional Cargo nightly features to build for Miden, so let's add a toolchain -file to our project root so that `rustup` and `cargo` will know what we need, and use them by -default: - - $ cat < rust-toolchain.toml - [toolchain] - channel = "nightly" - targets = "wasm32-unknown-unknown" - EOF - -Next, edit the `Cargo.toml` file as follows: - -```toml -[package] -name = "wasm-fib" -version = "0.1.0" -edition = "2021" - -[lib] -# Build this crate as a self-contained, C-style dynamic library -# This is required to emit the proper Wasm module type -crate-type = ["cdylib"] - -[dependencies] -# Use a tiny allocator in place of the default one, if we want -# to make use of types in the `alloc` crate, e.g. String. We -# don't need that now, but its good information to have in hand. -#wee_alloc = "0.4" - -# When we build for Wasm, we'll use the release profile -[profile.release] - -# Explicitly disable panic infrastructure on Wasm, as -# there is no proper support for them anyway, and it -# ensures that panics do not pull in a bunch of standard -# library code unintentionally -panic = "abort" - -# Optimize the output for size -opt-level = "z" -``` - -Most of these things are done to keep the generated code size as small as possible. Miden is a target -where the conventional wisdom about performance should be treated very carefully: we're almost always -going to benefit from less code, even if conventionally that code would be less efficient, simply due -to the difference in proving time accumulated due to extra instructions. That said, there are no hard -and fast rules, but these defaults are good ones to start with. - -**NOTE:** We recommended `wee_alloc` here, but any simple allocator will do, including a hand-written -bump allocator. The trade offs made by these small allocators are not generally suitable for long- -running, or allocation-heavy applications, as they "leak" memory (generally because they make little -to no attempt to recover freed allocations), however they are very useful for one-shot programs that -do minimal allocation, which is going to be the typical case for Miden programs. - -Next, edit `src/lib.rs` as shown below: - -```rust,noplayground -// This allows us to abort if the panic handler is invoked, but -// it is gated behind a perma-unstable nightly feature -#![feature(core_intrinsics)] -// Disable the warning triggered by the use of the `core_intrinsics` feature -#![allow(internal_features)] - -// Do not link against libstd (i.e. anything defined in `std::`) -#![no_std] - -// However, we could still use some standard library types while -// remaining no-std compatible, if we uncommented the following lines: -// -// extern crate alloc; -// use alloc::{string::String, vec::Vec}; - -// If we wanted to use the types mentioned above, it would also be -// a good idea to use the allocator we pulled in as a dependency -// in Cargo.toml, like so: -//#[global_allocator] -//static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; - -// Required for no-std crates -#[panic_handler] -fn panic(info: core::panic::PanicInfo) -> ! { - unsafe { core::intrinsics::abort() } -} - -// Marking the function no_mangle ensures that it is exported -// from the compiled binary as `fib`, otherwise it would have -// a mangled name that has no stable form. -// -// You can specify a different name from the library than the -// name in the source code using the `#[export_name = "foo"]` -// attribute, which will make the function callable as `foo` -// externally (in this example) -#[no_mangle] -pub fn fib(n: u32) -> u32 { - let mut a = 0; - let mut b = 1; - for _ in 0..n { - let c = a + b; - a = b; - b = c; - } - a -} -``` - -This exports our `fib` function from the library, making it callable from within a larger Miden program. - -All that remains is to compile to WebAssembly: - - $ cargo build --release --target=wasm32-unknown-unknown - -This places a `wasm_fib.wasm` file under the `target/wasm32-unknown-unknown/release/` directory, which -we can then examine with [wasm2wat](https://github.com/WebAssembly/wabt) to set the code we generated: - - $ wasm2wat target/wasm32-unknown-unknown/release/wasm_fib.wasm - -Which dumps the following output (may differ slightly on your machine, depending on the specific compiler version): - -```wat -(module - (type (;0;) (func (param i32) (result i32))) - (func $fib (type 0) (param i32) (result i32) - (local i32 i32 i32) - i32.const 0 - local.set 1 - i32.const 1 - local.set 2 - loop (result i32) ;; label = @1 - local.get 2 - local.set 3 - block ;; label = @2 - local.get 0 - br_if 0 (;@2;) - local.get 1 - return - end - local.get 0 - i32.const -1 - i32.add - local.set 0 - local.get 1 - local.get 3 - i32.add - local.set 2 - local.get 3 - local.set 1 - br 0 (;@1;) - end) - (memory (;0;) 16) - (global $__stack_pointer (mut i32) (i32.const 1048576)) - (global (;1;) i32 (i32.const 1048576)) - (global (;2;) i32 (i32.const 1048576)) - (export "memory" (memory 0)) - (export "fib" (func $fib)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2))) -``` - -Success! - -## Next Steps - -In the next chapter, we will walk through how to take the WebAssembly module we just compiled, and lower -it to Miden Assembly using `midenc`! diff --git a/docs/src/guides/wasm_to_masm.md b/docs/src/guides/wasm_to_masm.md deleted file mode 100644 index e1d5ab598..000000000 --- a/docs/src/guides/wasm_to_masm.md +++ /dev/null @@ -1,139 +0,0 @@ -# Compiling WebAssembly to Miden Assembly - -This chapter will walk you through compiling a WebAssembly (Wasm) module, in binary form -(i.e. a `.wasm` file), to an corresponding Miden Assembly (Masm) module (i.e. a `.masm` file). - -## Setup - -We will be making use of the example crate we created in [Compiling Rust to WebAssembly](rust_to_wasm.md), -which produces a small, lightweight Wasm module that is easy to examine in Wasm -text format, and demonstrates a good set of default choices for a project compiling -to Miden Assembly via WebAssembly. - -In this chapter, we will be compiling Wasm to MASM using the `midenc` executable, so ensure that -you have followed the instructions in the [Getting Started (midenc)](../usage/midenc.md) guide -and then return here. - -## Compiling to Miden Assembly - -In the last chapter, we compiled a Rust crate to WebAssembly that contains an implementation -of the Fibonacci function called `fib`, that was emitted to `target/wasm32-unknown-unknown/release/wasm_fib.wasm`. -All that remains is to tell `midenc` to compile this module to WebAssembly, as shown below: - -**NOTE:** The compiler is still under heavy development, so there are some known bugs that -may interfere with compilation depending on the flags you use - for the moment, the compiler -invocation we have to use is quite verbose, but this is a short term situation while we -address various other higher-priority tasks. Ultimately, using `midenc` directly will be -less common than other use cases (such as using `cargo miden`, or using the compiler as a -library for your own language frontend). - - $ midenc compile -o wasm_fib.masm --emit=masm target/wasm32-unknown-unknown/release/wasm_fib.wasm - -This will place the generated Miden Assembly code for our `wasm_fib` crate in the current directory. -If we dump the contents of this file, we'll see the following generated code: - -``` -export.fib - push.0 - push.1 - movup.2 - swap.1 - dup.1 - neq.0 - push.1 - while.true - if.true - push.4294967295 - movup.2 - swap.1 - u32wrapping_add - dup.1 - swap.1 - swap.3 - swap.1 - u32wrapping_add - movup.2 - swap.1 - dup.1 - neq.0 - push.1 - else - drop - drop - push.0 - end - end -end -``` - -If you compare this to the WebAssembly text format, you can see that this is a fairly -faithful translation, but there may be areas where we generate sub-optimal Miden Assembly. - -At the moment the compiler does only minimal optimization, late in the pipeline during codegen, -and only in regards to operand stack management. In other words, if you see an instruction -sequence you think is bad, certainly bring it to our attention, but we can't guarantee that -the code we generate will match what you would write by hand. - -## Testing with the Miden VM - -**NOTE: This example is more complicated than it needs to be at the moment, bear with us!** - -Assuming you have followed the instruction for installing the Miden VM locally, -we can test this program out as follows: - -First, we need to define a program to link our `wasm_fib.masm` module into, since -it is not a program, but a library module: - - $ cat < main.masm - use.wasm_fib::wasm_fib - - begin - exec.wasm_fib::fib - end - -We will also need a `.inputs` file to pass arguments to the program: - - $ cat < wasm_fib.inputs - { - "operand_stack": ["10"], - "advice_stack": ["0"], - } - -Next, we need to build a MASL library (normally `midenc` would do this, but there is a bug -blocking it at the moment, this example will be updated accordingly soon): - - $ mkdir -p wasm_fib && mv wasm_fib.masm wasm_fib/ - $ miden bundle -n wasm_fib wasm_fib - -With these in place, we can put it all together and run it: - - $ miden run -a main.masm -n 1 -i wasm_fib.inputs -l wasm_fib/wasm_fib.masl - ============================================================ - Run program - ============================================================ - Reading library file `wasm_fib/wasm_fib.masl` - Reading program file `main.masm` - Parsing program... done (0 ms) - Compiling program... done (2 ms) - Reading input file `wasm_fib.inputs` - Executing program with hash 3d965e7c6cfbcfe9d9db67262cbbc31517931a0169257f385d447d497cf55778... done (1 ms) - Output: [55] - VM cycles: 263 extended to 512 steps (48% padding). - ├── Stack rows: 263 - ├── Range checker rows: 67 - └── Chiplets rows: 201 - ├── Hash chiplet rows: 200 - ├── Bitwise chiplet rows: 0 - ├── Memory chiplet rows: 0 - └── Kernel ROM rows: 0 - - -Success! We got the expected result of `55`. - - -## Next Steps - -This guide is not comprehensive, as we have not yet examined in detail the differences between -compiling libraries vs programs, linking together multiple libraries, emitting a `.masl` library, -or discussed some of the compiler options. We will be updating this documentation with those -details and more in the coming days, so bear with us while we flesh out our guides! diff --git a/docs/src/usage/midenc.md b/docs/src/usage/midenc.md deleted file mode 100644 index bcae093c6..000000000 --- a/docs/src/usage/midenc.md +++ /dev/null @@ -1,70 +0,0 @@ -# As an Executable - -At the present time, we do not yet have prebuilt packages of the compiler toolchain -available, so it must be built from source, but the requirements for this are minimal, -as shown below: - -## Installation - -First, you'll need to have Rust installed (at time of writing, we're doing development -against Rust 1.74). - -Then, simply install `midenc` using Cargo, like so: - - # If you have cloned the git repo, and are in the project root: - $ cargo install --path midenc midenc - - # If you have Rust installed, but have not cloned the git repo: - $ cargo install --git https://github.com/0xpolygonmiden/compiler --branch develop midenc - -NOTE: This installation method relies on Cargo-managed binaries being in your shell `PATH`, -which is almost always the case, but if you have disabled this functionality, you'll need -to add `midenc` to your `PATH` manually. - -## Usage - -Once built, you should be able to invoke the compiler now, for example: - - $ midenc help compile - Usage: midenc compile [OPTIONS] [-- ...] - - Arguments: - [INPUTS]... - Path(s) to the source file(s) to compile. - - You may also use `-` as a file name to read a file from stdin. - - Options: - --output-dir - Write all compiler artifacts to DIR - - -W - Modify how warnings are treated by the compiler - - [default: auto] - - Possible values: - - none: Disable all warnings - - auto: Enable all warnings - - error: Promotes warnings to errors - - -v, --verbose - When set, produces more verbose output during compilation - - -h, --help - Print help (see a summary with '-h') - - -## Next Steps - -We currently have two frontends to the compiler, one that accepts the compiler's IR in textual -form (as a `.hir` file), primarily used for testing; and one that accepts a WebAssembly module -in binary form (i.e. as a `.wasm` file). - -For the vast majority of people, if not everyone, the `.wasm` form will be the one you are interested -in, so we have put together a [helpful guide](../guides/wasm_to_masm.md) that walks through how to -compile a WebAssembly module (in this case, produced by `rustc`) to Miden Assembly using `midenc`. - -If you aren't sure how to produce a WebAssembly module, you may be interested in -[another guide](../guides/rust_to_wasm.md) that demonstrates how to emit a WebAssembly module from -a Rust crate. diff --git a/frontend-wasm/Cargo.toml b/frontend-wasm/Cargo.toml deleted file mode 100644 index f15dfea46..000000000 --- a/frontend-wasm/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "miden-frontend-wasm" -description = "Build MidenIR from Wasm" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -miden-hir.workspace = true -miden-hir-type.workspace = true -miden-diagnostics.workspace = true -thiserror.workspace = true -smallvec.workspace = true -log.workspace = true -anyhow.workspace = true -wasmparser = "0.118.1" -derive_more = "0.99" -indexmap = "2.1" -gimli = { version = "0.28.0", default-features = false, features = ['read', 'std'] } - -[dev-dependencies] -wat = "1.0.69" -expect-test = "1.4.1" -miden-integration-tests.workspace = true diff --git a/frontend-wasm/src/code_translator/mod.rs b/frontend-wasm/src/code_translator/mod.rs deleted file mode 100644 index 5b47abf8e..000000000 --- a/frontend-wasm/src/code_translator/mod.rs +++ /dev/null @@ -1,1074 +0,0 @@ -//! This module contains the bulk of the code performing the translation between -//! WebAssembly and Miden IR. -//! -//! The translation is done in one pass, opcode by opcode. Two main data structures are used during -//! code translations: the value stack and the control stack. The value stack mimics the execution -//! of the WebAssembly stack machine: each instruction result is pushed onto the stack and -//! instruction arguments are popped off the stack. Similarly, when encountering a control flow -//! block, it is pushed onto the control stack and popped off when encountering the corresponding -//! `End`. -//! -//! Another data structure, the translation state, records information concerning unreachable code -//! status and about if inserting a return at the end of the function is necessary. -//! -//! Based on Cranelift's Wasm -> CLIF translator v11.0.0 - -use std::collections::{hash_map, HashMap}; -use std::u64; - -use crate::error::{WasmError, WasmResult}; -use crate::module::func_translation_state::{ControlStackFrame, ElseData, FuncTranslationState}; -use crate::module::function_builder_ext::FunctionBuilderExt; -use crate::module::types::{ir_type, BlockType, GlobalIndex, ModuleTypes}; -use crate::module::Module; -use crate::ssa::Variable; -use crate::unsupported_diag; -use miden_diagnostics::{DiagnosticsHandler, SourceSpan}; -use miden_hir::cranelift_entity::packed_option::ReservedValue; -use miden_hir::Type::*; -use miden_hir::{Block, Inst, InstBuilder, Value}; -use miden_hir::{Immediate, Type}; -use wasmparser::{MemArg, Operator}; - -#[cfg(test)] -mod tests; - -#[cfg(test)] -mod tests_unsupported; - -/// Translates wasm operators into Miden IR instructions. -pub fn translate_operator( - op: &Operator, - builder: &mut FunctionBuilderExt, - state: &mut FuncTranslationState, - mod_info: &Module, - mod_types: &ModuleTypes, - diagnostics: &DiagnosticsHandler, - span: SourceSpan, -) -> WasmResult<()> { - if !state.reachable { - translate_unreachable_operator(&op, builder, state, mod_types, span)?; - return Ok(()); - } - - // Given that we believe the current block is reachable, the FunctionBuilderExt ought to agree. - debug_assert!(!builder.is_unreachable()); - - match op { - /********************************** Locals **************************************** - * `get_local` and `set_local` are treated as non-SSA variables and will completely - * disappear in the Miden IR - ***********************************************************************************/ - Operator::LocalGet { local_index } => { - let val = builder.use_var(Variable::from_u32(*local_index)); - state.push1(val); - } - Operator::LocalSet { local_index } => { - let val = state.pop1(); - builder.def_var(Variable::from_u32(*local_index), val); - } - Operator::LocalTee { local_index } => { - let val = state.peek1(); - builder.def_var(Variable::from_u32(*local_index), val); - } - /********************************** Globals ****************************************/ - Operator::GlobalGet { global_index } => { - let global_index = GlobalIndex::from_u32(*global_index); - let name = mod_info.name_section.globals_names[&global_index].clone(); - let ty = ir_type(mod_info.globals[global_index].ty)?; - state.push1(builder.ins().load_symbol(name, ty, span)); - } - Operator::GlobalSet { global_index } => { - let global_index = GlobalIndex::from_u32(*global_index); - let name = mod_info.name_section.globals_names[&global_index].clone(); - let ty = ir_type(mod_info.globals[global_index].ty)?; - let ptr = builder - .ins() - .symbol_addr(name, Ptr(ty.clone().into()), span); - let val = state.pop1(); - builder.ins().store(ptr, val, span); - } - /********************************* Stack misc **************************************/ - Operator::Drop => _ = state.pop1(), - Operator::Select => { - let (arg1, arg2, cond) = state.pop3(); - // if cond is not 0, return arg1, else return arg2 - // https://www.w3.org/TR/wasm-core-1/#-hrefsyntax-instr-parametricmathsfselect%E2%91%A0 - let cond_i1 = builder.ins().neq_imm(cond, Immediate::I32(0), span); - state.push1(builder.ins().select(cond_i1, arg1, arg2, span)); - } - Operator::Unreachable => { - builder.ins().unreachable(span); - state.reachable = false; - } - 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::Else => translate_else(state, builder, span)?, - Operator::End => translate_end(state, builder, span), - - /**************************** Branch instructions *********************************/ - Operator::Br { relative_depth } => translate_br(state, relative_depth, builder, span), - Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, state, span), - Operator::BrTable { targets } => translate_br_table(targets, state, builder, span)?, - Operator::Return => translate_return(state, builder, diagnostics, span)?, - /************************************ Calls ****************************************/ - Operator::Call { function_index } => { - translate_call( - state, - builder, - function_index, - mod_info, - mod_types, - span, - diagnostics, - )?; - } - /******************************* Memory management *********************************/ - Operator::MemoryGrow { .. } => { - let arg = state.pop1_casted(U32, builder, span); - state.push1(builder.ins().mem_grow(arg, span)); - } - Operator::MemorySize { .. } => { - // Return total Miden memory size - state.push1(builder.ins().i32(mem_total_pages(), span)); - } - /******************************* Load instructions ***********************************/ - Operator::I32Load8U { memarg } => { - translate_load_zext(U8, I32, memarg, state, builder, span) - } - Operator::I32Load16U { memarg } => { - translate_load_zext(U16, I32, memarg, state, builder, span) - } - Operator::I32Load8S { memarg } => { - translate_load_sext(I8, I32, memarg, state, builder, span); - } - Operator::I32Load16S { memarg } => { - translate_load_sext(I16, I32, memarg, state, builder, span); - } - Operator::I64Load8U { memarg } => { - translate_load_zext(U8, I64, memarg, state, builder, span) - } - Operator::I64Load16U { memarg } => { - translate_load_zext(U16, I64, memarg, state, builder, span) - } - Operator::I64Load8S { memarg } => { - translate_load_sext(I8, I64, memarg, state, builder, span); - } - Operator::I64Load16S { memarg } => { - translate_load_sext(I16, I64, memarg, state, builder, span); - } - Operator::I64Load32S { memarg } => { - translate_load_sext(I32, I64, memarg, state, builder, span) - } - Operator::I64Load32U { memarg } => { - translate_load_zext(U32, I64, memarg, state, builder, span) - } - Operator::I32Load { memarg } => translate_load(I32, memarg, state, builder, span), - Operator::I64Load { memarg } => translate_load(I64, memarg, state, builder, span), - /****************************** Store instructions ***********************************/ - Operator::I32Store { memarg } => translate_store(I32, memarg, state, builder, span), - Operator::I64Store { memarg } => translate_store(I64, memarg, state, builder, span), - Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { - translate_store(U8, memarg, state, builder, span); - } - Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { - translate_store(U16, memarg, state, builder, span); - } - Operator::I64Store32 { memarg } => translate_store(U32, memarg, state, builder, span), - /****************************** Nullary Operators **********************************/ - Operator::I32Const { value } => state.push1(builder.ins().i32(*value, span)), - Operator::I64Const { value } => state.push1(builder.ins().i64(*value, span)), - - /******************************* Unary Operators *************************************/ - Operator::I32Clz | Operator::I32Ctz => { - // Temporary workaround to allow further code translations - // until clz and ctz are available in Miden IR - // TODO: use the `clz` and `ctz` instructions when they are available - let val = state.pop1(); - state.push1(builder.ins().popcnt(val, span)); - } - Operator::I32Popcnt | Operator::I64Popcnt => { - let val = state.pop1(); - state.push1(builder.ins().popcnt(val, span)); - } - Operator::I32Extend8S | Operator::I32Extend16S => { - let val = state.pop1(); - state.push1(builder.ins().sext(val, I32, span)); - } - Operator::I64ExtendI32S => { - let val = state.pop1(); - state.push1(builder.ins().sext(val, I64, span)); - } - Operator::I64ExtendI32U => { - let val = state.pop1(); - let u32_val = builder.ins().cast(val, U32, span); - let u64_val = builder.ins().zext(u32_val, U64, span); - let i64_val = builder.ins().cast(u64_val, I64, span); - state.push1(i64_val); - } - Operator::I32WrapI64 => { - let val = state.pop1(); - state.push1(builder.ins().trunc(val, I32, span)); - } - /****************************** Binary Operators ************************************/ - Operator::I32Add | Operator::I64Add => { - let (arg1, arg2) = state.pop2(); - // wrapping because the result is mod 2^N - // https://www.w3.org/TR/wasm-core-1/#op-iadd - state.push1(builder.ins().add_wrapping(arg1, arg2, span)); - } - Operator::I32And | Operator::I64And => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().band(arg1, arg2, span)); - } - Operator::I32Or | Operator::I64Or => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().bor(arg1, arg2, span)); - } - Operator::I32Xor | Operator::I64Xor => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().bxor(arg1, arg2, span)); - } - Operator::I32Shl | Operator::I64Shl => { - let (arg1, arg2) = state.pop2(); - // wrapping shift semantics drop any bits that would cause - // the shift to exceed the bitwidth of the type - state.push1(builder.ins().shl_wrapping(arg1, arg2, span)); - } - Operator::I32ShrU => { - let (arg1, arg2) = state.pop2_casted(U32, builder, span); - // wrapping shift semantics drop any bits that would cause - // the shift to exceed the bitwidth of the type - let val = builder.ins().shr_wrapping(arg1, arg2, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64ShrU => { - let (arg1, arg2) = state.pop2_casted(U64, builder, span); - // wrapping shift semantics drop any bits that would cause - // the shift to exceed the bitwidth of the type - let val = builder.ins().shr_wrapping(arg1, arg2, span); - state.push1(builder.ins().cast(val, I64, span)); - } - Operator::I32ShrS | Operator::I64ShrS => { - let (arg1, arg2) = state.pop2(); - // wrapping shift semantics drop any bits that would cause - // the shift to exceed the bitwidth of the type - state.push1(builder.ins().shr_wrapping(arg1, arg2, span)); - } - Operator::I32Rotl | Operator::I64Rotl => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().rotl(arg1, arg2, span)); - } - Operator::I32Rotr | Operator::I64Rotr => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().rotr(arg1, arg2, span)); - } - Operator::I32Sub | Operator::I64Sub => { - let (arg1, arg2) = state.pop2(); - // wrapping because the result is mod 2^N - // https://www.w3.org/TR/wasm-core-1/#op-isub - state.push1(builder.ins().sub_wrapping(arg1, arg2, span)); - } - Operator::F64Sub => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().sub_checked(arg1, arg2, span)); - } - Operator::I32Mul | Operator::I64Mul => { - let (arg1, arg2) = state.pop2(); - // wrapping because the result is mod 2^N - // https://www.w3.org/TR/wasm-core-1/#op-imul - state.push1(builder.ins().mul_wrapping(arg1, arg2, span)); - } - Operator::I32DivS | Operator::I64DivS => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().div_checked(arg1, arg2, span)); - } - Operator::I32DivU => { - let (arg1, arg2) = state.pop2_casted(U32, builder, span); - let val = builder.ins().div_checked(arg1, arg2, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64DivU => { - let (arg1, arg2) = state.pop2_casted(U64, builder, span); - let val = builder.ins().div_checked(arg1, arg2, span); - state.push1(builder.ins().cast(val, I64, span)); - } - Operator::I32RemU => { - let (arg1, arg2) = state.pop2_casted(U32, builder, span); - let val = builder.ins().r#mod_checked(arg1, arg2, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64RemU => { - let (arg1, arg2) = state.pop2_casted(U64, builder, span); - let val = builder.ins().r#mod_checked(arg1, arg2, span); - state.push1(builder.ins().cast(val, I64, span)); - } - Operator::I32RemS | Operator::I64RemS => { - let (arg1, arg2) = state.pop2(); - state.push1(builder.ins().r#mod_checked(arg1, arg2, span)); - } - /**************************** Comparison Operators **********************************/ - Operator::I32LtU => { - let (arg0, arg1) = state.pop2_casted(U32, builder, span); - let val = builder.ins().lt(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64LtU => { - let (arg0, arg1) = state.pop2_casted(U64, builder, span); - let val = builder.ins().lt(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32LtS => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().lt(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64LtS => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().lt(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32LeU => { - let (arg0, arg1) = state.pop2_casted(U32, builder, span); - let val = builder.ins().lte(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64LeU => { - let (arg0, arg1) = state.pop2_casted(U64, builder, span); - let val = builder.ins().lte(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32LeS => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().lte(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64LeS => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().lte(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32GtU => { - let (arg0, arg1) = state.pop2_casted(U32, builder, span); - let val = builder.ins().gt(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64GtU => { - let (arg0, arg1) = state.pop2_casted(U64, builder, span); - let val = builder.ins().gt(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32GtS | Operator::I64GtS => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().gt(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32GeU => { - let (arg0, arg1) = state.pop2_casted(U32, builder, span); - let val = builder.ins().gte(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64GeU => { - let (arg0, arg1) = state.pop2_casted(U64, builder, span); - let val = builder.ins().gte(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32GeS => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().gte(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64GeS => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().gte(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32Eqz => { - let arg = state.pop1(); - let val = builder.ins().eq_imm(arg, Immediate::I32(0), span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64Eqz => { - let arg = state.pop1(); - let val = builder.ins().eq_imm(arg, Immediate::I64(0), span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32Eq => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().eq(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64Eq => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().eq(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I32Ne => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().neq(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - Operator::I64Ne => { - let (arg0, arg1) = state.pop2(); - let val = builder.ins().neq(arg0, arg1, span); - state.push1(builder.ins().cast(val, I32, span)); - } - op => { - unsupported_diag!(diagnostics, "Wasm op {:?} is not supported", op); - } - }; - 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 = HashMap::new(); - 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(()) -} - -/// Return the total Miden VM memory size (2^32 addresses * word (4 felts) bytes) in 64KB pages -const fn mem_total_pages() -> i32 { - const FELT_BYTES: u64 = 4; // felts are effectively 32 bits - const WORD_BYTES: u64 = 4 * FELT_BYTES; // 4 felts per word - const PAGE_SIZE: u64 = 64 * 1024; - const MEMORY_SIZE: u64 = u32::MAX as u64 * WORD_BYTES; - (MEMORY_SIZE / PAGE_SIZE) as i32 -} - -fn translate_load( - ptr_ty: Type, - memarg: &MemArg, - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) { - let addr_int = state.pop1(); - let addr = prepare_addr(addr_int, &ptr_ty, memarg, builder, span); - state.push1(builder.ins().load(addr, span)); -} - -fn translate_load_sext( - ptr_ty: Type, - sext_ty: Type, - memarg: &MemArg, - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) { - let addr_int = state.pop1(); - let addr = prepare_addr(addr_int, &ptr_ty, memarg, builder, span); - let val = builder.ins().load(addr, span); - let sext_val = builder.ins().sext(val, sext_ty, span); - state.push1(sext_val); -} - -fn translate_load_zext( - ptr_ty: Type, - zext_ty: Type, - memarg: &MemArg, - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) { - assert!(ptr_ty.is_unsigned_integer()); - let addr_int = state.pop1(); - let addr = prepare_addr(addr_int, &ptr_ty, memarg, builder, span); - let val = builder.ins().load(addr, span); - let sext_val = builder.ins().zext(val, zext_ty, span); - state.push1(sext_val); -} - -fn translate_store( - ptr_ty: Type, - memarg: &MemArg, - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) { - let (addr_int, val) = state.pop2(); - let val_ty = builder.data_flow_graph().value_type(val); - let arg = if ptr_ty != *val_ty { - builder.ins().trunc(val, ptr_ty.clone(), span) - } else { - val - }; - let addr = prepare_addr(addr_int, &ptr_ty, memarg, builder, span); - builder.ins().store(addr, arg, span); -} - -fn prepare_addr( - addr_int: Value, - ptr_ty: &Type, - memarg: &MemArg, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) -> Value { - let addr_int_ty = builder.data_flow_graph().value_type(addr_int); - let addr_u32 = if *addr_int_ty == U32 { - addr_int - } else { - builder.ins().cast(addr_int, U32, span) - }; - let full_addr_int = if memarg.offset != 0 { - builder - .ins() - .add_imm_checked(addr_u32, Immediate::U32(memarg.offset as u32), span) - } else { - addr_u32 - }; - builder - .ins() - .inttoptr(full_addr_int, Type::Ptr(ptr_ty.clone().into()), span) -} - -fn translate_call( - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - function_index: &u32, - mod_info: &Module, - mod_types: &ModuleTypes, - span: SourceSpan, - diagnostics: &DiagnosticsHandler, -) -> WasmResult<()> { - let (fident, num_args) = state.get_direct_func( - builder.data_flow_graph_mut(), - *function_index, - mod_info, - mod_types, - diagnostics, - )?; - let args = state.peekn_mut(num_args); - let call = builder.ins().call(fident, &args, span); - let inst_results = builder.inst_results(call); - state.popn(num_args); - state.pushn(inst_results); - Ok(()) -} - -fn translate_return( - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - diagnostics: &DiagnosticsHandler, - span: SourceSpan, -) -> WasmResult<()> { - let return_count = { - let frame = &mut state.control_stack[0]; - frame.num_return_values() - }; - { - let return_args = match return_count { - 0 => None, - 1 => Some(state.peekn_mut(return_count).first().unwrap().clone()), - _ => { - unsupported_diag!(diagnostics, "Multiple values are not supported"); - } - }; - - builder.ins().ret(return_args, span); - } - state.popn(return_count); - state.reachable = false; - Ok(()) -} - -fn translate_br( - state: &mut FuncTranslationState, - relative_depth: &u32, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) { - let i = state.control_stack.len() - 1 - (*relative_depth as usize); - let (return_count, br_destination) = { - let frame = &mut state.control_stack[i]; - // We signal that all the code that follows until the next End is unreachable - frame.set_branched_to_exit(); - let return_count = if frame.is_loop() { - frame.num_param_values() - } else { - frame.num_return_values() - }; - (return_count, frame.br_destination()) - }; - let destination_args = state.peekn_mut(return_count); - builder.ins().br(br_destination, &destination_args, span); - state.popn(return_count); - state.reachable = false; -} - -fn translate_br_if( - relative_depth: u32, - builder: &mut FunctionBuilderExt, - state: &mut FuncTranslationState, - span: SourceSpan, -) { - let cond = state.pop1(); - let (br_destination, inputs) = translate_br_if_args(relative_depth, state); - let next_block = builder.create_block(); - let then_dest = br_destination; - let then_args = inputs; - let else_dest = next_block; - let else_args = &[]; - let cond_i1 = builder.ins().neq_imm(cond, Immediate::I32(0), span); - builder - .ins() - .cond_br(cond_i1, then_dest, then_args, else_dest, else_args, span); - builder.seal_block(next_block); // The only predecessor is the current block. - builder.switch_to_block(next_block); -} - -fn translate_br_if_args( - relative_depth: u32, - state: &mut FuncTranslationState, -) -> (Block, &mut [Value]) { - let i = state.control_stack.len() - 1 - (relative_depth as usize); - let (return_count, br_destination) = { - let frame = &mut state.control_stack[i]; - // The values returned by the branch are still available for the reachable - // code that comes after it - frame.set_branched_to_exit(); - let return_count = if frame.is_loop() { - frame.num_param_values() - } else { - frame.num_return_values() - }; - (return_count, frame.br_destination()) - }; - let inputs = state.peekn_mut(return_count); - (br_destination, inputs) -} - -fn translate_block( - blockty: &wasmparser::BlockType, - builder: &mut FunctionBuilderExt, - state: &mut FuncTranslationState, - mod_types: &ModuleTypes, - span: SourceSpan, -) -> WasmResult<()> { - let blockty = BlockType::from_wasm(blockty, mod_types)?; - let next = builder.create_block_with_params(blockty.results.clone(), span); - state.push_block(next, blockty.params.len(), blockty.results.len()); - Ok(()) -} - -fn translate_end( - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) { - // The `End` instruction pops the last control frame from the control stack, seals - // the destination block (since `br` instructions targeting it only appear inside the - // block and have already been translated) and modify the value stack to use the - // possible `Block`'s arguments values. - let frame = state.control_stack.pop().unwrap(); - let next_block = frame.following_code(); - let return_count = frame.num_return_values(); - let return_args = state.peekn_mut(return_count); - - builder.ins().br(next_block, return_args, span); - - // You might expect that if we just finished an `if` block that - // didn't have a corresponding `else` block, then we would clean - // up our duplicate set of parameters that we pushed earlier - // right here. However, we don't have to explicitly do that, - // since we truncate the stack back to the original height - // below. - - builder.switch_to_block(next_block); - builder.seal_block(next_block); - - // If it is a loop we also have to seal the body loop block - if let ControlStackFrame::Loop { header, .. } = frame { - builder.seal_block(header) - } - - frame.truncate_value_stack_to_original_size(&mut state.stack); - state - .stack - .extend_from_slice(builder.block_params(next_block)); -} - -fn translate_else( - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) -> WasmResult<()> { - let i = state.control_stack.len() - 1; - Ok(match state.control_stack[i] { - ControlStackFrame::If { - ref else_data, - head_is_reachable, - ref mut consequent_ends_reachable, - num_return_values, - ref blocktype, - destination, - .. - } => { - // We finished the consequent, so record its final - // reachability state. - debug_assert!(consequent_ends_reachable.is_none()); - *consequent_ends_reachable = Some(state.reachable); - - if head_is_reachable { - // We have a branch from the head of the `if` to the `else`. - state.reachable = true; - - // Ensure we have a block for the `else` block (it may have - // already been pre-allocated, see `ElseData` for details). - let else_block = match *else_data { - ElseData::NoElse { - branch_inst, - placeholder, - } => { - debug_assert_eq!(blocktype.params.len(), num_return_values); - let else_block = - builder.create_block_with_params(blocktype.params.clone(), span); - let params_len = blocktype.params.len(); - builder.ins().br(destination, state.peekn(params_len), span); - state.popn(params_len); - - builder.change_jump_destination(branch_inst, placeholder, else_block); - builder.seal_block(else_block); - else_block - } - ElseData::WithElse { else_block } => { - builder - .ins() - .br(destination, state.peekn(num_return_values), span); - state.popn(num_return_values); - else_block - } - }; - - // You might be expecting that we push the parameters for this - // `else` block here, something like this: - // - // state.pushn(&control_stack_frame.params); - // - // We don't do that because they are already on the top of the stack - // for us: we pushed the parameters twice when we saw the initial - // `if` so that we wouldn't have to save the parameters in the - // `ControlStackFrame` as another `Vec` allocation. - - builder.switch_to_block(else_block); - - // We don't bother updating the control frame's `ElseData` - // to `WithElse` because nothing else will read it. - } - } - _ => unreachable!(), - }) -} - -fn translate_if( - blockty: &wasmparser::BlockType, - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - mod_types: &ModuleTypes, - span: SourceSpan, -) -> WasmResult<()> { - let blockty = BlockType::from_wasm(blockty, mod_types)?; - let cond = state.pop1(); - let cond_i1 = builder.ins().neq_imm(cond, Immediate::I32(0), span); - let next_block = builder.create_block(); - let (destination, else_data) = if blockty.params.eq(&blockty.results) { - // It is possible there is no `else` block, so we will only - // allocate a block for it if/when we find the `else`. For now, - // we if the condition isn't true, then we jump directly to the - // destination block following the whole `if...end`. If we do end - // up discovering an `else`, then we will allocate a block for it - // and go back and patch the jump. - let destination = builder.create_block_with_params(blockty.results.clone(), span); - let branch_inst = builder.ins().cond_br( - cond_i1, - next_block, - &[], - destination, - state.peekn(blockty.params.len()), - span, - ); - ( - destination, - ElseData::NoElse { - branch_inst, - placeholder: destination, - }, - ) - } else { - // The `if` type signature is not valid without an `else` block, - // so we eagerly allocate the `else` block here. - let destination = builder.create_block_with_params(blockty.results.clone(), span); - let else_block = builder.create_block_with_params(blockty.params.clone(), span); - builder.ins().cond_br( - cond_i1, - next_block, - &[], - else_block, - state.peekn(blockty.params.len()), - span, - ); - builder.seal_block(else_block); - (destination, ElseData::WithElse { else_block }) - }; - builder.seal_block(next_block); - builder.switch_to_block(next_block); - state.push_if( - destination, - else_data, - blockty.params.len(), - blockty.results.len(), - blockty, - ); - Ok(()) -} - -fn translate_loop( - blockty: &wasmparser::BlockType, - builder: &mut FunctionBuilderExt, - state: &mut FuncTranslationState, - mod_types: &ModuleTypes, - span: SourceSpan, -) -> WasmResult<()> { - let blockty = BlockType::from_wasm(blockty, mod_types)?; - 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); - state.push_loop(loop_body, next, blockty.params.len(), blockty.results.len()); - state.popn(blockty.params.len()); - state - .stack - .extend_from_slice(builder.block_params(loop_body)); - builder.switch_to_block(loop_body); - Ok(()) -} - -/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them -/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable -/// portion so the translation state must be updated accordingly. -fn translate_unreachable_operator( - op: &Operator, - builder: &mut FunctionBuilderExt, - state: &mut FuncTranslationState, - mod_types: &ModuleTypes, - span: SourceSpan, -) -> WasmResult<()> { - debug_assert!(!state.reachable); - match *op { - 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)?; - state.push_if( - Block::reserved_value(), - ElseData::NoElse { - branch_inst: Inst::reserved_value(), - placeholder: Block::reserved_value(), - }, - 0, - 0, - blockty, - ); - } - Operator::Loop { blockty: _ } | Operator::Block { blockty: _ } => { - state.push_block(Block::reserved_value(), 0, 0); - } - Operator::Else => { - let i = state.control_stack.len() - 1; - match state.control_stack[i] { - ControlStackFrame::If { - ref else_data, - head_is_reachable, - ref mut consequent_ends_reachable, - ref blocktype, - .. - } => { - debug_assert!(consequent_ends_reachable.is_none()); - *consequent_ends_reachable = Some(state.reachable); - - if head_is_reachable { - // We have a branch from the head of the `if` to the `else`. - state.reachable = true; - - let else_block = match *else_data { - ElseData::NoElse { - branch_inst, - placeholder, - } => { - let else_block = builder - .create_block_with_params(blocktype.params.clone(), span); - let frame = state.control_stack.last().unwrap(); - frame.truncate_value_stack_to_else_params(&mut state.stack); - - // We change the target of the branch instruction. - builder.change_jump_destination( - branch_inst, - placeholder, - else_block, - ); - builder.seal_block(else_block); - else_block - } - ElseData::WithElse { else_block } => { - let frame = state.control_stack.last().unwrap(); - frame.truncate_value_stack_to_else_params(&mut state.stack); - else_block - } - }; - - builder.switch_to_block(else_block); - - // Again, no need to push the parameters for the `else`, - // since we already did when we saw the original `if`. See - // the comment for translating `Operator::Else` in - // `translate_operator` for details. - } - } - _ => unreachable!(), - } - } - Operator::End => { - let stack = &mut state.stack; - let control_stack = &mut state.control_stack; - let frame = control_stack.pop().unwrap(); - - // Pop unused parameters from stack. - frame.truncate_value_stack_to_original_size(stack); - - let reachable_anyway = match frame { - // If it is a loop we also have to seal the body loop block - ControlStackFrame::Loop { header, .. } => { - builder.seal_block(header); - // And loops can't have branches to the end. - false - } - // If we never set `consequent_ends_reachable` then that means - // we are finishing the consequent now, and there was no - // `else`. Whether the following block is reachable depends only - // on if the head was reachable. - ControlStackFrame::If { - head_is_reachable, - consequent_ends_reachable: None, - .. - } => head_is_reachable, - // Since we are only in this function when in unreachable code, - // we know that the alternative just ended unreachable. Whether - // the following block is reachable depends on if the consequent - // ended reachable or not. - ControlStackFrame::If { - head_is_reachable, - consequent_ends_reachable: Some(consequent_ends_reachable), - .. - } => head_is_reachable && consequent_ends_reachable, - // All other control constructs are already handled. - _ => false, - }; - - if frame.exit_is_branched_to() || reachable_anyway { - builder.switch_to_block(frame.following_code()); - builder.seal_block(frame.following_code()); - - // And add the return values of the block but only if the next block is reachable - // (which corresponds to testing if the stack depth is 1) - stack.extend_from_slice(builder.block_params(frame.following_code())); - state.reachable = true; - } - } - _ => { - // We don't translate because this is unreachable code - } - } - - Ok(()) -} diff --git a/frontend-wasm/src/code_translator/tests.rs b/frontend-wasm/src/code_translator/tests.rs deleted file mode 100644 index 86e721d23..000000000 --- a/frontend-wasm/src/code_translator/tests.rs +++ /dev/null @@ -1,1739 +0,0 @@ -use expect_test::expect; -use miden_hir::write_instruction; -use miden_hir::Ident; - -use crate::test_utils::test_diagnostics; -use crate::translate_module; -use crate::WasmTranslationConfig; - -/// Compiles the given Wasm code to Miden IR and checks the IR generated. -fn check_ir(wat: &str, expected_ir: expect_test::Expect) { - let wasm = wat::parse_str(wat).unwrap(); - let diagnostics = test_diagnostics(); - let module = translate_module(&wasm, &WasmTranslationConfig::default(), &diagnostics).unwrap(); - expected_ir.assert_eq(&module.to_string()); -} - -/// 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 wat = format!( - r#" - (module - (memory (;0;) 16384) - (func $test_wrapper - {wat_op} - ) - )"#, - ); - let wasm = wat::parse_str(wat).unwrap(); - let diagnostics = test_diagnostics(); - let module = translate_module(&wasm, &WasmTranslationConfig::default(), &diagnostics).unwrap(); - let func = module.function(Ident::from("test_wrapper")).unwrap(); - // let fref = module.get_funcref_by_name("test_wrapper").unwrap(); - // let func = module.get_function(fref).unwrap(); - let entry_block = func.dfg.entry_block(); - // let entry_block_data = func.dfg.block_data(entry_block); - let entry_block_data = func.dfg.block(entry_block); - let mut w = String::new(); - // print instructions up to the branch to the exit block - for inst in entry_block_data - .insts() - .take_while(|inst| !func.dfg[*inst].opcode().is_branch()) - { - write_instruction(&mut w, func, inst, 0).unwrap(); - } - expected_ir.assert_eq(&w); -} - -#[test] -fn module() { - check_ir( - r#" - (module - (func $main - i32.const 0 - drop - ) - ) - "#, - expect![[r#" - module noname - - pub fn main() { - block0: - v0 = const.i32 0 : i32; - br block1; - - block1: - ret; - } - "#]], - ); -} - -#[test] -fn locals() { - check_ir( - r#" - (module - (func $main (local i32) - i32.const 1 - local.set 0 - local.get 0 - drop - ) - ) - "#, - expect![[r#" - module noname - - pub fn main() { - block0: - v0 = const.i32 0 : i32; - v1 = const.i32 1 : i32; - br block1; - - block1: - ret; - } - "#]], - ); -} - -#[test] -fn locals_inter_block() { - check_ir( - r#" - (module - (func $main (result i32) (local i32) - block - i32.const 3 - local.set 0 - end - block - local.get 0 - i32.const 5 - i32.add - local.set 0 - end - i32.const 7 - local.get 0 - i32.add - ) - ) - "#, - expect![[r#" - module noname - - pub fn main() -> i32 { - block0: - v1 = const.i32 0 : i32; - v2 = const.i32 3 : i32; - br block2; - - block1(v0: i32): - ret v0; - - block2: - v3 = const.i32 5 : i32; - v4 = add.wrapping v2, v3 : i32; - br block3; - - block3: - v5 = const.i32 7 : i32; - v6 = add.wrapping v5, v4 : i32; - br block1(v6); - } - "#]], - ); -} - -#[test] -fn func_call() { - check_ir( - r#" - (module - (func $add (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.add - ) - (func $main (result i32) - i32.const 3 - i32.const 5 - call $add - ) - ) - "#, - expect![[r#" - module noname - - pub fn add(i32, i32) -> i32 { - block0(v0: i32, v1: i32): - v3 = add.wrapping v0, v1 : i32; - br block1(v3); - - block1(v2: i32): - ret v2; - } - - pub fn main() -> i32 { - block0: - v1 = const.i32 3 : i32; - v2 = const.i32 5 : i32; - v3 = call noname::add(v1, v2) : i32; - br block1(v3); - - block1(v0: i32): - ret v0; - } - "#]], - ); -} - -#[test] -fn br() { - check_ir( - r#" - (module - (func $main (result i32) (local i32) - block - i32.const 3 - local.set 0 - br 0 - end - local.get 0 - ) - ) - "#, - expect![[r#" - module noname - - pub fn main() -> i32 { - block0: - v1 = const.i32 0 : i32; - v2 = const.i32 3 : i32; - br block2; - - block1(v0: i32): - ret v0; - - block2: - br block1(v2); - } - "#]], - ); -} - -#[test] -fn loop_br_if() { - // sum the decreasing numbers from 2 to 0, i.e. 2 + 1 + 0, then exit the loop - check_ir( - r#" - (module - (func $main (result i32) (local i32 i32) - i32.const 2 - local.set 0 - loop - local.get 0 - local.get 1 - i32.add - local.set 1 - local.get 0 - i32.const 1 - i32.sub - local.tee 0 - br_if 0 - end - local.get 1 - ) - ) - "#, - expect![[r#" - module noname - - pub fn main() -> i32 { - block0: - v1 = const.i32 0 : i32; - v2 = const.i32 2 : i32; - br block2(v2, v1); - - block1(v0: i32): - ret v0; - - block2(v3: i32, v4: i32): - v5 = add.wrapping v3, v4 : i32; - v6 = const.i32 1 : i32; - v7 = sub.wrapping v3, v6 : i32; - v8 = neq v7, 0 : i1; - condbr v8, block2(v7, v5), block4; - - block3: - br block1(v5); - - block4: - br block3; - } - "#]], - ); -} - -#[test] -fn if_then_else() { - check_ir( - r#" - (module - (func $main (result i32) - i32.const 2 - if (result i32) - i32.const 3 - else - i32.const 5 - end - ) - ) - "#, - expect![[r#" - module noname - - pub fn main() -> i32 { - block0: - v1 = const.i32 2 : i32; - v2 = neq v1, 0 : i1; - condbr v2, block2, block4; - - block1(v0: i32): - ret v0; - - block2: - v4 = const.i32 3 : i32; - br block3(v4); - - block3(v3: i32): - br block1(v3); - - block4: - v5 = const.i32 5 : i32; - br block3(v5); - } - "#]], - ); -} - -#[test] -fn global_var() { - check_ir( - r#" - (module - (global $MyGlobalVal (mut i32) i32.const 42) - (func $main - global.get $MyGlobalVal - i32.const 9 - i32.add - global.set $MyGlobalVal - ) - ) - "#, - expect![[r#" - module noname - - const $0 = 0x0000002a; - - global external @MyGlobalVal : i32 = $0 { id = 0 }; - - pub fn main() { - block0: - v0 = global.load (@MyGlobalVal) as *mut i8 : i32; - v1 = const.i32 9 : i32; - v2 = add.wrapping v0, v1 : i32; - v3 = global.symbol @MyGlobalVal : *mut i32; - store v3, v2; - br block1; - - block1: - ret; - } - "#]], - ); -} - -#[test] -fn memory_grow() { - check_op( - r#" - i32.const 1 - memory.grow - drop - "#, - expect![[r#" - v0 = const.i32 1 : i32; - v1 = cast v0 : u32; - v2 = memory.grow v1 : i32; - "#]], - ) -} - -#[test] -fn memory_size() { - check_op( - r#" - memory.size - drop - "#, - expect![[r#" - v0 = const.i32 1048575 : i32; - "#]], - ) -} - -#[test] -fn i32_load8_u() { - check_op( - r#" - i32.const 1024 - i32.load8_u - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut u8; - v3 = load v2 : u8; - v4 = zext v3 : i32; - "#]], - ) -} - -#[test] -fn i32_load16_u() { - check_op( - r#" - i32.const 1024 - i32.load16_u - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut u16; - v3 = load v2 : u16; - v4 = zext v3 : i32; - "#]], - ) -} - -#[test] -fn i32_load8_s() { - check_op( - r#" - i32.const 1024 - i32.load8_s - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut i8; - v3 = load v2 : i8; - v4 = sext v3 : i32; - "#]], - ) -} - -#[test] -fn i32_load16_s() { - check_op( - r#" - i32.const 1024 - i32.load16_s - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut i16; - v3 = load v2 : i16; - v4 = sext v3 : i32; - "#]], - ) -} - -#[test] -fn i64_load8_u() { - check_op( - r#" - i32.const 1024 - i64.load8_u - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut u8; - v3 = load v2 : u8; - v4 = zext v3 : i64; - "#]], - ) -} - -#[test] -fn i64_load16_u() { - check_op( - r#" - i32.const 1024 - i64.load16_u - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut u16; - v3 = load v2 : u16; - v4 = zext v3 : i64; - "#]], - ) -} - -#[test] -fn i64_load8_s() { - check_op( - r#" - i32.const 1024 - i64.load8_s - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut i8; - v3 = load v2 : i8; - v4 = sext v3 : i64; - "#]], - ) -} - -#[test] -fn i64_load16_s() { - check_op( - r#" - i32.const 1024 - i64.load16_s - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut i16; - v3 = load v2 : i16; - v4 = sext v3 : i64; - "#]], - ) -} - -#[test] -fn i64_load32_s() { - check_op( - r#" - i32.const 1024 - i64.load32_s - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut i32; - v3 = load v2 : i32; - v4 = sext v3 : i64; - "#]], - ) -} - -#[test] -fn i64_load32_u() { - check_op( - r#" - i32.const 1024 - i64.load32_u - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut u32; - v3 = load v2 : u32; - v4 = zext v3 : i64; - "#]], - ) -} - -#[test] -fn i32_load() { - check_op( - r#" - i32.const 1024 - i32.load - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut i32; - v3 = load v2 : i32; - "#]], - ) -} - -#[test] -fn i64_load() { - check_op( - r#" - i32.const 1024 - i64.load - drop - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = cast v0 : u32; - v2 = inttoptr v1 : *mut i64; - v3 = load v2 : i64; - "#]], - ) -} - -#[test] -fn i32_store() { - check_op( - r#" - i32.const 1024 - i32.const 1 - i32.store - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = const.i32 1 : i32; - v2 = cast v0 : u32; - v3 = inttoptr v2 : *mut i32; - store v3, v1; - "#]], - ) -} - -#[test] -fn i64_store() { - check_op( - r#" - i32.const 1024 - i64.const 1 - i64.store - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = const.i64 1 : i64; - v2 = cast v0 : u32; - v3 = inttoptr v2 : *mut i64; - store v3, v1; - "#]], - ) -} - -#[test] -fn i32_store8() { - check_op( - r#" - i32.const 1024 - i32.const 1 - i32.store8 - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = const.i32 1 : i32; - v2 = trunc v1 : u8; - v3 = cast v0 : u32; - v4 = inttoptr v3 : *mut u8; - store v4, v2; - "#]], - ) -} - -#[test] -fn i32_store16() { - check_op( - r#" - i32.const 1024 - i32.const 1 - i32.store16 - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = const.i32 1 : i32; - v2 = trunc v1 : u16; - v3 = cast v0 : u32; - v4 = inttoptr v3 : *mut u16; - store v4, v2; - "#]], - ) -} - -#[test] -fn i64_store32() { - check_op( - r#" - i32.const 1024 - i64.const 1 - i64.store32 - "#, - expect![[r#" - v0 = const.i32 1024 : i32; - v1 = const.i64 1 : i64; - v2 = trunc v1 : u32; - v3 = cast v0 : u32; - v4 = inttoptr v3 : *mut u32; - store v4, v2; - "#]], - ) -} - -#[test] -fn i32_const() { - check_op( - r#" - i32.const 1 - drop - "#, - expect![[r#" - v0 = const.i32 1 : i32; - "#]], - ) -} - -#[test] -fn i64_const() { - check_op( - r#" - i64.const 1 - drop - "#, - expect![[r#" - v0 = const.i64 1 : i64; - "#]], - ) -} - -#[test] -fn i32_popcnt() { - check_op( - r#" - i32.const 1 - i32.popcnt - drop - "#, - expect![[r#" - v0 = const.i32 1 : i32; - v1 = popcnt v0 : i32; - "#]], - ) -} - -#[test] -fn i64_extend_i32_s() { - check_op( - r#" - i32.const 1 - i64.extend_i32_s - drop - "#, - expect![[r#" - v0 = const.i32 1 : i32; - v1 = sext v0 : i64; - "#]], - ) -} - -#[test] -fn i64_extend_i32_u() { - check_op( - r#" - i32.const 1 - i64.extend_i32_u - drop - "#, - expect![[r#" - v0 = const.i32 1 : i32; - v1 = cast v0 : u32; - v2 = zext v1 : u64; - v3 = cast v2 : i64; - "#]], - ) -} - -#[test] -fn i32_wrap_i64() { - check_op( - r#" - i64.const 1 - i32.wrap_i64 - drop - "#, - expect![[r#" - v0 = const.i64 1 : i64; - v1 = trunc v0 : i32; - "#]], - ) -} - -#[test] -fn i32_add() { - check_op( - r#" - i32.const 3 - i32.const 1 - i32.add - drop - "#, - expect![[r#" - v0 = const.i32 3 : i32; - v1 = const.i32 1 : i32; - v2 = add.wrapping v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_add() { - check_op( - r#" - i64.const 3 - i64.const 1 - i64.add - drop - "#, - expect![[r#" - v0 = const.i64 3 : i64; - v1 = const.i64 1 : i64; - v2 = add.wrapping v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_and() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.and - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = band v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_and() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.and - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = band v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_or() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.or - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = bor v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_or() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.or - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = bor v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_sub() { - check_op( - r#" - i32.const 3 - i32.const 1 - i32.sub - drop - "#, - expect![[r#" - v0 = const.i32 3 : i32; - v1 = const.i32 1 : i32; - v2 = sub.wrapping v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_sub() { - check_op( - r#" - i64.const 3 - i64.const 1 - i64.sub - drop - "#, - expect![[r#" - v0 = const.i64 3 : i64; - v1 = const.i64 1 : i64; - v2 = sub.wrapping v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_xor() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.xor - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = bxor v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_xor() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.xor - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = bxor v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_shl() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.shl - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = shl.wrapping v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_shl() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.shl - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = shl.wrapping v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_shr_u() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.shr_u - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = cast v0 : u32; - v3 = cast v1 : u32; - v4 = shr.wrapping v2, v3 : u32; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i64_shr_u() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.shr_u - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = cast v0 : u64; - v3 = cast v1 : u64; - v4 = shr.wrapping v2, v3 : u64; - v5 = cast v4 : i64; - "#]], - ) -} - -#[test] -fn i32_shr_s() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.shr_s - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = shr.wrapping v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_shr_s() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.shr_s - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = shr.wrapping v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_rotl() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.rotl - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = rotl v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_rotl() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.rotl - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = rotl v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_rotr() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.rotr - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = rotr v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_rotr() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.rotr - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = rotr v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_mul() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.mul - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = mul.wrapping v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_mul() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.mul - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = mul.wrapping v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_div_u() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.div_u - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = cast v0 : u32; - v3 = cast v1 : u32; - v4 = div.checked v2, v3 : u32; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i64_div_u() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.div_u - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = cast v0 : u64; - v3 = cast v1 : u64; - v4 = div.checked v2, v3 : u64; - v5 = cast v4 : i64; - "#]], - ) -} - -#[test] -fn i32_div_s() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.div_s - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = div.checked v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_div_s() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.div_s - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = div.checked v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_rem_u() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.rem_u - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = cast v0 : u32; - v3 = cast v1 : u32; - v4 = mod.checked v2, v3 : u32; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i64_rem_u() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.rem_u - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = cast v0 : u64; - v3 = cast v1 : u64; - v4 = mod.checked v2, v3 : u64; - v5 = cast v4 : i64; - "#]], - ) -} - -#[test] -fn i32_rem_s() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.rem_s - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = mod.checked v0, v1 : i32; - "#]], - ) -} - -#[test] -fn i64_rem_s() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.rem_s - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = mod.checked v0, v1 : i64; - "#]], - ) -} - -#[test] -fn i32_lt_u() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.lt_u - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = cast v0 : u32; - v3 = cast v1 : u32; - v4 = lt v2, v3 : i1; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i64_lt_u() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.lt_u - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = cast v0 : u64; - v3 = cast v1 : u64; - v4 = lt v2, v3 : i1; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i32_lt_s() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.lt_s - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = lt v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i64_lt_s() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.lt_s - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = lt v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i32_le_u() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.le_u - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = cast v0 : u32; - v3 = cast v1 : u32; - v4 = lte v2, v3 : i1; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i64_le_u() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.le_u - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = cast v0 : u64; - v3 = cast v1 : u64; - v4 = lte v2, v3 : i1; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i32_le_s() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.le_s - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = lte v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i64_le_s() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.le_s - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = lte v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i32_gt_u() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.gt_u - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = cast v0 : u32; - v3 = cast v1 : u32; - v4 = gt v2, v3 : i1; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i64_gt_u() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.gt_u - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = cast v0 : u64; - v3 = cast v1 : u64; - v4 = gt v2, v3 : i1; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i32_gt_s() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.gt_s - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = gt v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i64_gt_s() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.gt_s - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = gt v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i32_ge_u() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.ge_u - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = cast v0 : u32; - v3 = cast v1 : u32; - v4 = gte v2, v3 : i1; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i64_ge_u() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.ge_u - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = cast v0 : u64; - v3 = cast v1 : u64; - v4 = gte v2, v3 : i1; - v5 = cast v4 : i32; - "#]], - ) -} - -#[test] -fn i32_ge_s() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.ge_s - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = gte v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i64_ge_s() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.ge_s - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = gte v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i32_eqz() { - check_op( - r#" - i32.const 2 - i32.eqz - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = eq v0, 0 : i1; - v2 = cast v1 : i32; - "#]], - ) -} - -#[test] -fn i64_eqz() { - check_op( - r#" - i64.const 2 - i64.eqz - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = eq v0, 0 : i1; - v2 = cast v1 : i32; - "#]], - ) -} - -#[test] -fn i32_eq() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.eq - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = eq v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i64_eq() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.eq - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = eq v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i32_ne() { - check_op( - r#" - i32.const 2 - i32.const 1 - i32.ne - drop - "#, - expect![[r#" - v0 = const.i32 2 : i32; - v1 = const.i32 1 : i32; - v2 = neq v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn i64_ne() { - check_op( - r#" - i64.const 2 - i64.const 1 - i64.ne - drop - "#, - expect![[r#" - v0 = const.i64 2 : i64; - v1 = const.i64 1 : i64; - v2 = neq v0, v1 : i1; - v3 = cast v2 : i32; - "#]], - ) -} - -#[test] -fn select_i32() { - check_op( - r#" - i64.const 3 - i64.const 7 - i32.const 42 - select - drop - "#, - expect![[r#" - v0 = const.i64 3 : i64; - v1 = const.i64 7 : i64; - v2 = const.i32 42 : i32; - v3 = neq v2, 0 : i1; - v4 = select v3, v0, v1 : i64; - "#]], - ) -} diff --git a/frontend-wasm/src/code_translator/tests_unsupported.rs b/frontend-wasm/src/code_translator/tests_unsupported.rs deleted file mode 100644 index 25d0dd937..000000000 --- a/frontend-wasm/src/code_translator/tests_unsupported.rs +++ /dev/null @@ -1,192 +0,0 @@ -use miden_diagnostics::SourceSpan; -use miden_hir::CallConv; -use miden_hir::Linkage; -use miden_hir::ModuleBuilder; -use miden_hir::Signature; - -use wasmparser::MemArg; -use wasmparser::Operator; -use wasmparser::Operator::*; - -use crate::module::func_translation_state::FuncTranslationState; -use crate::module::function_builder_ext::FunctionBuilderContext; -use crate::module::function_builder_ext::FunctionBuilderExt; -use crate::module::Module; -use crate::test_utils::test_diagnostics; - -use super::translate_operator; - -fn check_unsupported(op: &Operator) { - let diagnostics = test_diagnostics(); - let mod_name = "noname"; - let module_info = Module::new(); - let mut module_builder = ModuleBuilder::new(mod_name); - let sig = Signature { - params: vec![], - results: vec![], - cc: CallConv::SystemV, - linkage: Linkage::External, - }; - let mut module_func_builder = module_builder.function("func_name", sig.clone()).unwrap(); - let mut fb_ctx = FunctionBuilderContext::new(); - let mut state = FuncTranslationState::new(); - let mut builder_ext = FunctionBuilderExt::new(&mut module_func_builder, &mut fb_ctx); - let mod_types = Default::default(); - let result = translate_operator( - op, - &mut builder_ext, - &mut state, - &module_info, - &mod_types, - &diagnostics, - SourceSpan::default(), - ); - assert!( - result.is_err(), - "Expected unsupported op error for {:?}", - op - ); - assert_eq!( - 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 -const UNSUPPORTED_WASM_V1_OPS: &[Operator] = &[ - CallIndirect { - type_index: 0, - table_index: 0, - table_byte: 0, - }, - /****************************** Memory Operators ************************************/ - F32Load { - memarg: MemArg { - align: 0, - max_align: 0, - offset: 0, - memory: 0, - }, - }, - F64Load { - memarg: MemArg { - align: 0, - max_align: 0, - offset: 0, - memory: 0, - }, - }, - F32Store { - memarg: MemArg { - align: 0, - max_align: 0, - offset: 0, - memory: 0, - }, - }, - F64Store { - memarg: MemArg { - align: 0, - max_align: 0, - offset: 0, - memory: 0, - }, - }, - /****************************** Nullary Operators ************************************/ - - // Cannot construct since Ieee32 fields are private - // F32Const { - // value: Ieee32(0), - // }, - // F64Const { - // value: Ieee32(0), - // }, - - /****************************** Unary Operators ************************************/ - // I32Ctz, - // I32Clz, - I64Ctz, - I64Clz, - F32Sqrt, - F64Sqrt, - F32Ceil, - F64Ceil, - F32Floor, - F64Floor, - F32Trunc, - F64Trunc, - F32Nearest, - F64Nearest, - F32Abs, - F64Abs, - F32Neg, - F64Neg, - F64ConvertI64U, - F64ConvertI32U, - F64ConvertI64S, - F64ConvertI32S, - F32ConvertI64S, - F32ConvertI32S, - F32ConvertI64U, - F32ConvertI32U, - F64PromoteF32, - F32DemoteF64, - I64TruncF64S, - I64TruncF32S, - I32TruncF64S, - I32TruncF32S, - I64TruncF64U, - I64TruncF32U, - I32TruncF64U, - I32TruncF32U, - I64TruncSatF64S, - I64TruncSatF32S, - I32TruncSatF64S, - I32TruncSatF32S, - I64TruncSatF64U, - I64TruncSatF32U, - I32TruncSatF64U, - I32TruncSatF32U, - F32ReinterpretI32, - F64ReinterpretI64, - I32ReinterpretF32, - I64ReinterpretF64, - /****************************** Binary Operators ************************************/ - F32Add, - F32Sub, - F32Mul, - F32Div, - F32Min, - F32Max, - F32Copysign, - F64Copysign, - F64Add, - F64Mul, - F64Div, - F64Min, - F64Max, - /**************************** Comparison Operators **********************************/ - F32Eq, - F32Ne, - F32Gt, - F32Ge, - F32Le, - F32Lt, - F64Eq, - F64Ne, - F64Gt, - F64Ge, - F64Le, - F64Lt, -]; - -#[test] -fn error_for_unsupported_wasm_v1_ops() { - for op in UNSUPPORTED_WASM_V1_OPS.iter() { - check_unsupported(op); - } -} diff --git a/frontend-wasm/src/component/info.rs b/frontend-wasm/src/component/info.rs deleted file mode 100644 index 58845e9b9..000000000 --- a/frontend-wasm/src/component/info.rs +++ /dev/null @@ -1,7 +0,0 @@ -/// Possible encodings of strings within the component model. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum StringEncoding { - Utf8, - Utf16, - CompactUtf16, -} diff --git a/frontend-wasm/src/component/mod.rs b/frontend-wasm/src/component/mod.rs deleted file mode 100644 index 45c2cbff5..000000000 --- a/frontend-wasm/src/component/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Support for the Wasm component model translation -//! -//! This module contains all of the internal type definitions to parse and -//! translate the component model. - -mod info; -pub mod translate; -mod types; - -pub use self::info::*; -pub use self::types::*; diff --git a/frontend-wasm/src/component/translate.rs b/frontend-wasm/src/component/translate.rs deleted file mode 100644 index aef79afd8..000000000 --- a/frontend-wasm/src/component/translate.rs +++ /dev/null @@ -1,967 +0,0 @@ -//! Translation of a Wasm component from binary into an intermediate form -//! suitable for further processing and IR module generation - -// Based on wasmtime v16.0 Wasm component translation - -use crate::error::WasmResult; -use crate::module::module_env::{ModuleEnvironment, ModuleTranslation}; -use crate::module::types::{ - convert_func_type, convert_valtype, EntityIndex, FuncIndex, GlobalIndex, MemoryIndex, - ModuleTypesBuilder, SignatureIndex, TableIndex, WasmType, -}; -use crate::{component::*, unsupported_diag, WasmTranslationConfig}; -use indexmap::IndexMap; -use miden_diagnostics::DiagnosticsHandler; -use miden_hir::cranelift_entity::PrimaryMap; -use std::collections::HashMap; -use std::mem; -use wasmparser::types::{ - AliasableResourceId, ComponentEntityType, ComponentFuncTypeId, ComponentInstanceTypeId, Types, -}; -use wasmparser::{Chunk, ComponentImportName, Encoding, Parser, Payload, Validator, WasmFeatures}; - -/// Translate a Wasm component binary into Miden IR module -pub fn translate_component( - wasm: &[u8], - config: WasmTranslationConfig, - diagnostics: &DiagnosticsHandler, -) -> WasmResult { - let wasm_features = WasmFeatures::all(); - let mut validator = wasmparser::Validator::new_with_features(wasm_features); - let mut types = Default::default(); - let translator = Translator::new(config, &mut validator, &mut types); - translator.translate(wasm, diagnostics) -} - -/// Structure used to parse a Wasm component and translate it into an IR `Module` -pub struct Translator<'a, 'data> { - /// Configuration options for the translation. - config: WasmTranslationConfig, - - /// The current component being translated. - /// - /// This will get swapped out as translation traverses the body of a - /// component and a sub-component is entered or left. - result: Translation<'data>, - - /// Current state of parsing a binary component. Note that like `result` - /// this will change as the component is traversed. - parser: Parser, - - /// Stack of lexical scopes that are in-progress but not finished yet. - /// - /// This is pushed to whenever a component is entered and popped from - /// whenever a component is left. Each lexical scope also contains - /// information about the variables that it is currently required to close - /// over which is threaded into the current in-progress translation of - /// the sub-component which pushed a scope here. - lexical_scopes: Vec>, - - /// The validator in use to verify that the raw input binary is a valid - /// component. - validator: &'a mut Validator, - - /// Type information shared for the entire component. - /// - /// This builder is also used for all core wasm modules found to intern - /// signatures across all modules. - types: &'a mut ModuleTypesBuilder, - - /// Completely translated core wasm modules that have been found so far. - /// - /// Note that this translation only involves learning about type - /// information and functions are not actually translated here. - static_modules: PrimaryMap>, - - /// Completely translated components that have been found so far. - /// - /// As frames are popped from `lexical_scopes` their completed component - /// will be pushed onto this list. - static_components: PrimaryMap>, -} - -/// Representation of the syntactic scope of a component meaning where it is -/// and what its state is at in the binary format. -/// -/// These scopes are pushed and popped when a sub-component starts being -/// parsed and finishes being parsed. The main purpose of this frame is to -/// have a `ClosedOverVars` field which encapsulates data that is inherited -/// from the scope specified into the component being translated just beneath -/// it. -/// -/// This structure exists to implement outer aliases to components and modules. -/// When a component or module is closed over then that means it needs to be -/// inherited in a sense to the component which actually had the alias. This is -/// achieved with a deceptively simple scheme where each parent of the -/// component with the alias will inherit the component from the desired -/// location. -/// -/// For example with a component structure that looks like: -/// -/// ```wasm -/// (component $A -/// (core module $M) -/// (component $B -/// (component $C -/// (alias outer $A $M (core module)) -/// ) -/// ) -/// ) -/// ``` -/// -/// here the `C` component is closing over `M` located in the root component -/// `A`. When `C` is being translated the `lexical_scopes` field will look like -/// `[A, B]`. When the alias is encountered (for module index 0) this will -/// place a `ClosedOverModule::Local(0)` entry into the `closure_args` field of -/// `A`'s frame. This will in turn give a `ModuleUpvarIndex` which is then -/// inserted into `closure_args` in `B`'s frame. This produces yet another -/// `ModuleUpvarIndex` which is finally inserted into `C`'s module index space -/// via `LocalInitializer::AliasModuleUpvar` with the last index. -/// -/// All of these upvar indices and such are interpreted in the "inline" phase of -/// translation. This means that when `A` is being instantiated one of its -/// initializers will be `LocalInitializer::ComponentStatic`. This starts to -/// create `B` and the variables captured for `B` are listed as local module 0, -/// or `M`. This list is then preserved in the definition of the component `B` -/// and later reused by `C` again to finally get access to the closed over -/// component. -/// -/// Effectively the scopes are managed hierarchically where a reference to an -/// outer variable automatically injects references into all parents up to -/// where the reference is. This variable scopes are then processed during -/// inlining where a component definition is a reference to the static -/// component information (`Translation`) plus closed over variables -/// (`ComponentClosure` during inlining). -struct LexicalScope<'data> { - /// Current state of translating the `translation` below. - parser: Parser, - /// Current state of the component's translation as found so far. - translation: Translation<'data>, - /// List of captures that `translation` will need to process to create the - /// - /// sub-component which is directly beneath this lexical scope. - closure_args: ClosedOverVars, -} - -/// A "local" translation of a component. -/// -/// This structure is used as a sort of in-progress translation of a component. -#[derive(Default)] -struct Translation<'data> { - /// Instructions which form this component. - /// - /// There is one initializer for all members of each index space, and all - /// index spaces are incrementally built here as the initializer list is - /// processed. - initializers: Vec>, - - /// The list of exports from this component, as pairs of names and an - /// index into an index space of what's being exported. - exports: IndexMap<&'data str, ComponentItem>, - - /// Type information produced by `wasmparser` for this component. - /// - /// This type information is available after the translation of the entire - /// component has finished, e.g. for the `inline` pass, but beforehand this - /// is set to `None`. - types: Option, -} - -// NB: the type information contained in `LocalInitializer` should always point -// to `wasmparser`'s type information, not Wasmtime's. Component types cannot be -// fully determined due to resources until instantiations are known which is -// tracked during the inlining phase. This means that all type information below -// is straight from `wasmparser`'s passes. -enum LocalInitializer<'data> { - // imports - Import(ComponentImportName<'data>, ComponentEntityType), - - // canonical function sections - Lower { - func: ComponentFuncIndex, - lower_ty: ComponentFuncTypeId, - canonical_abi: SignatureIndex, - options: LocalCanonicalOptions, - }, - Lift(ComponentFuncTypeId, FuncIndex, LocalCanonicalOptions), - - // resources - Resource(AliasableResourceId, WasmType, Option), - ResourceNew(AliasableResourceId, SignatureIndex), - ResourceRep(AliasableResourceId, SignatureIndex), - ResourceDrop(AliasableResourceId, SignatureIndex), - - // core wasm modules - ModuleStatic(StaticModuleIndex), - - // core wasm module instances - ModuleInstantiate(ModuleIndex, HashMap<&'data str, ModuleInstanceIndex>), - ModuleSynthetic(HashMap<&'data str, EntityIndex>), - - // components - ComponentStatic(StaticComponentIndex, ClosedOverVars), - - // component instances - ComponentInstantiate( - ComponentIndex, - HashMap<&'data str, ComponentItem>, - ComponentInstanceTypeId, - ), - ComponentSynthetic(HashMap<&'data str, ComponentItem>), - - // alias section - AliasExportFunc(ModuleInstanceIndex, &'data str), - AliasExportTable(ModuleInstanceIndex, &'data str), - AliasExportGlobal(ModuleInstanceIndex, &'data str), - AliasExportMemory(ModuleInstanceIndex, &'data str), - AliasComponentExport(ComponentInstanceIndex, &'data str), - AliasModule(ClosedOverModule), - AliasComponent(ClosedOverComponent), - - // export section - Export(ComponentItem), -} - -/// The "closure environment" of components themselves. -/// -/// For more information see `LexicalScope`. -#[derive(Default)] -struct ClosedOverVars { - components: PrimaryMap, - modules: PrimaryMap, -} - -/// Description how a component is closed over when the closure variables for -/// a component are being created. -/// -/// For more information see `LexicalScope`. -enum ClosedOverComponent { - /// A closed over component is coming from the local component's index - /// space, meaning a previously defined component is being captured. - Local(ComponentIndex), - /// A closed over component is coming from our own component's list of - /// upvars. This list was passed to us by our enclosing component, which - /// will eventually have bottomed out in closing over a `Local` component - /// index for some parent component. - Upvar(ComponentUpvarIndex), -} - -/// Same as `ClosedOverComponent`, but for modules. -enum ClosedOverModule { - Local(ModuleIndex), - Upvar(ModuleUpvarIndex), -} - -/// Representation of canonical ABI options. -struct LocalCanonicalOptions { - string_encoding: StringEncoding, - memory: Option, - realloc: Option, - post_return: Option, -} - -/// Action to take after parsing a payload. -enum Action { - /// Keep going with parsing the input data. - KeepGoing, - /// Skip `n` bytes of input data (e.g. a module section which was parsed separately). - Skip(usize), - /// Parsing is done when there is no parent component in the scope stack. - Done, -} - -impl<'a, 'data> Translator<'a, 'data> { - /// Creates a new translation state ready to translate a component. - pub fn new( - config: WasmTranslationConfig, - validator: &'a mut Validator, - types: &'a mut ModuleTypesBuilder, - ) -> Self { - Self { - config, - result: Translation::default(), - validator, - types, - parser: Parser::new(0), - lexical_scopes: Vec::new(), - static_components: Default::default(), - static_modules: Default::default(), - } - } - - /// Translates the binary `component`. - /// - /// This is the workhorse of the translation which will parse all of `component` - /// and create type information. The `component` does not have to be valid - /// and it will be validated during compilation. - pub fn translate( - mut self, - component: &'data [u8], - diagnostics: &DiagnosticsHandler, - ) -> WasmResult { - self.parse(component, diagnostics)?; - - todo!("translate the parsed Wasm component to IR Module"); - } - - /// Parses the given the Wasm component into an intermediate `Translation` in self.result. - fn parse( - &mut self, - component: &'data [u8], - diagnostics: &DiagnosticsHandler, - ) -> Result<(), crate::WasmError> { - let mut remaining = component; - loop { - let payload = match self.parser.parse(remaining, true)? { - Chunk::Parsed { payload, consumed } => { - remaining = &remaining[consumed..]; - payload - } - Chunk::NeedMoreData(_) => unreachable!(), - }; - - match self.parse_payload(payload, component, diagnostics)? { - Action::KeepGoing => {} - Action::Skip(n) => remaining = &remaining[n..], - Action::Done => break, - } - } - assert!(remaining.is_empty()); - assert!(self.lexical_scopes.is_empty()); - Ok(()) - } - - /// Parses a given payload from the Wasm component. - fn parse_payload( - &mut self, - payload: Payload<'data>, - component: &'data [u8], - diagnostics: &DiagnosticsHandler, - ) -> WasmResult { - match payload { - Payload::Version { - num, - encoding, - range, - } => { - self.validator.version(num, encoding, &range)?; - - match encoding { - Encoding::Component => {} - Encoding::Module => { - panic!("attempted to parse a wasm module with a component parser"); - } - } - } - Payload::End(offset) => { - assert!(self.result.types.is_none()); - self.result.types = Some(self.validator.end(offset)?); - // Exit the current lexical scope. If there is no parent (no - // frame currently on the stack) then translation is finished. - // Otherwise that means that a nested component has been - // completed and is recorded as such. - let LexicalScope { - parser, - translation, - closure_args, - } = match self.lexical_scopes.pop() { - Some(frame) => frame, - None => return Ok(Action::Done), - }; - self.parser = parser; - let component = mem::replace(&mut self.result, translation); - let static_idx = self.static_components.push(component); - self.result - .initializers - .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::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)?; - return Ok(Action::Skip(range.end - range.start)); - } - Payload::ComponentSection { parser, 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)?; - unimplemented!("component start section"); - } - Payload::ComponentAliasSection(s) => self.component_alias_section(s)?, - // All custom sections are ignored at this time. - // and parse a `name` section here. - Payload::CustomSection { .. } => {} - // Anything else is either not reachable since we never enable the - // feature or we do enable it and it's a bug we don't - // implement it, so let validation take care of most errors here and - // if it gets past validation provide a helpful error message to - // debug. - other => { - self.validator.payload(&other)?; - unsupported_diag!(diagnostics, "unsupported section {other:?}"); - } - } - - Ok(Action::KeepGoing) - } - - fn component_type_section( - &mut self, - s: wasmparser::ComponentTypeSectionReader<'data>, - ) -> Result<(), crate::WasmError> { - // 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. - // - // Note that the push/pop of the component types scope happens above in - // `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)?; - let types = self.validator.types(0).unwrap(); - Ok(for ty in s { - match ty? { - wasmparser::ComponentType::Resource { rep, dtor } => { - let rep = convert_valtype(rep); - let id = types - .component_any_type_at(component_type_index) - .unwrap_resource(); - let dtor = dtor.map(FuncIndex::from_u32); - self.result - .initializers - .push(LocalInitializer::Resource(id, rep, dtor)); - } - - // no extra processing needed - wasmparser::ComponentType::Defined(_) - | wasmparser::ComponentType::Func(_) - | wasmparser::ComponentType::Instance(_) - | wasmparser::ComponentType::Component(_) => {} - } - - component_type_index += 1; - }) - } - - fn component_import_section( - &mut self, - s: wasmparser::ComponentImportSectionReader<'data>, - ) -> Result<(), crate::WasmError> { - // 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)?; - Ok(for import in s { - let import = import?; - 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)); - }) - } - - fn component_canonical_section( - &mut self, - s: wasmparser::ComponentCanonicalSectionReader<'data>, - ) -> Result<(), crate::WasmError> { - // 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)?; - Ok(for func in s { - let types = self.validator.types(0).unwrap(); - let init = match func? { - wasmparser::CanonicalFunction::Lift { - type_index, - core_func_index, - options, - } => { - let ty = types.component_any_type_at(type_index).unwrap_func(); - let func = FuncIndex::from_u32(core_func_index); - let options = canonical_options(&options); - LocalInitializer::Lift(ty, func, options) - } - wasmparser::CanonicalFunction::Lower { - func_index, - options, - } => { - let lower_ty = types.component_function_at(func_index); - let func = ComponentFuncIndex::from_u32(func_index); - let options = canonical_options(&options); - let canonical_abi = self.core_func_signature(core_func_index); - - core_func_index += 1; - LocalInitializer::Lower { - func, - options, - canonical_abi, - lower_ty, - } - } - wasmparser::CanonicalFunction::ResourceNew { resource } => { - let resource = types.component_any_type_at(resource).unwrap_resource(); - let ty = self.core_func_signature(core_func_index); - core_func_index += 1; - LocalInitializer::ResourceNew(resource, ty) - } - wasmparser::CanonicalFunction::ResourceDrop { resource } => { - let resource = types.component_any_type_at(resource).unwrap_resource(); - let ty = self.core_func_signature(core_func_index); - core_func_index += 1; - LocalInitializer::ResourceDrop(resource, ty) - } - wasmparser::CanonicalFunction::ResourceRep { resource } => { - let resource = types.component_any_type_at(resource).unwrap_resource(); - let ty = self.core_func_signature(core_func_index); - core_func_index += 1; - LocalInitializer::ResourceRep(resource, ty) - } - }; - self.result.initializers.push(init); - }) - } - - fn module_section( - &mut self, - range: std::ops::Range, - parser: Parser, - component: &'data [u8], - diagnostics: &DiagnosticsHandler, - ) -> Result<(), crate::WasmError> { - // Core wasm modules are translated 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 - // of the input are skipped. - // - // 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)?; - let translation = ModuleEnvironment::new(&self.config, self.validator, self.types).parse( - parser, - &component[range.start..range.end], - diagnostics, - )?; - let static_idx = self.static_modules.push(translation); - self.result - .initializers - .push(LocalInitializer::ModuleStatic(static_idx)); - Ok(()) - } - - fn component_section( - &mut self, - range: std::ops::Range, - parser: Parser, - ) -> Result<(), crate::WasmError> { - // When a sub-component is found then the current translation state - // is pushed onto the `lexical_scopes` stack. This will subsequently - // get popped as part of `Payload::End` processing above. - // - // Note that the set of closure args for this new lexical scope - // starts empty since it will only get populated if translation of - // the nested component ends up aliasing some outer module or - // component. - self.validator.component_section(&range)?; - self.lexical_scopes.push(LexicalScope { - parser: mem::replace(&mut self.parser, parser), - translation: mem::take(&mut self.result), - closure_args: ClosedOverVars::default(), - }); - Ok(()) - } - - fn core_instance_section( - &mut self, - s: wasmparser::InstanceSectionReader<'data>, - ) -> Result<(), crate::WasmError> { - // 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)?; - Ok(for instance in s { - let init = match instance? { - wasmparser::Instance::Instantiate { module_index, args } => { - let index = ModuleIndex::from_u32(module_index); - instantiate_module(index, &args) - } - wasmparser::Instance::FromExports(exports) => { - instantiate_module_from_exports(&exports) - } - }; - self.result.initializers.push(init); - }) - } - - fn component_instance_section( - &mut self, - s: wasmparser::ComponentInstanceSectionReader<'data>, - ) -> Result<(), crate::WasmError> { - let mut index = self.validator.types(0).unwrap().component_instance_count(); - self.validator.component_instance_section(&s)?; - Ok(for instance in s { - let init = match instance? { - wasmparser::ComponentInstance::Instantiate { - component_index, - args, - } => { - let types = self.validator.types(0).unwrap(); - let ty = types.component_instance_at(index); - let index = ComponentIndex::from_u32(component_index); - self.instantiate_component(index, &args, ty)? - } - wasmparser::ComponentInstance::FromExports(exports) => { - self.instantiate_component_from_exports(&exports)? - } - }; - self.result.initializers.push(init); - index += 1; - }) - } - - fn component_export_section( - &mut self, - s: wasmparser::ComponentExportSectionReader<'data>, - ) -> Result<(), crate::WasmError> { - // Exports don't actually fill out the `initializers` array but - // instead fill out the one other field in a `Translation`, 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)?; - Ok(for export in s { - let export = export?; - let item = self.kind_to_item(export.kind, export.index)?; - let prev = self.result.exports.insert(export.name.0, item); - assert!(prev.is_none()); - self.result - .initializers - .push(LocalInitializer::Export(item)); - }) - } - - fn component_alias_section( - &mut self, - s: wasmparser::ComponentAliasSectionReader<'data>, - ) -> Result<(), crate::WasmError> { - // 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)?; - Ok(for alias in s { - let init = match alias? { - wasmparser::ComponentAlias::InstanceExport { - kind: _, - instance_index, - name, - } => { - let instance = ComponentInstanceIndex::from_u32(instance_index); - LocalInitializer::AliasComponentExport(instance, name) - } - wasmparser::ComponentAlias::Outer { kind, count, index } => { - self.alias_component_outer(kind, count, index); - continue; - } - wasmparser::ComponentAlias::CoreInstanceExport { - kind, - instance_index, - name, - } => { - let instance = ModuleInstanceIndex::from_u32(instance_index); - alias_module_instance_export(kind, instance, name) - } - }; - self.result.initializers.push(init); - }) - } - - /// Parses a component instance - fn instantiate_component( - &mut self, - component: ComponentIndex, - raw_args: &[wasmparser::ComponentInstantiationArg<'data>], - ty: ComponentInstanceTypeId, - ) -> WasmResult> { - let mut args = HashMap::with_capacity(raw_args.len()); - for arg in raw_args { - let idx = self.kind_to_item(arg.kind, arg.index)?; - args.insert(arg.name, idx); - } - - Ok(LocalInitializer::ComponentInstantiate(component, args, ty)) - } - - /// Creates a synthetic module from the list of items currently in the - /// module and their given names. - fn instantiate_component_from_exports( - &mut self, - exports: &[wasmparser::ComponentExport<'data>], - ) -> WasmResult> { - let mut map = HashMap::with_capacity(exports.len()); - for export in exports { - let idx = self.kind_to_item(export.kind, export.index)?; - map.insert(export.name.0, idx); - } - - Ok(LocalInitializer::ComponentSynthetic(map)) - } - - /// Converts wasmparser's `ComponentExternalKind` into our `ComponentItem`. - fn kind_to_item( - &mut self, - kind: wasmparser::ComponentExternalKind, - index: u32, - ) -> WasmResult { - Ok(match kind { - wasmparser::ComponentExternalKind::Func => { - let index = ComponentFuncIndex::from_u32(index); - ComponentItem::Func(index) - } - wasmparser::ComponentExternalKind::Module => { - let index = ModuleIndex::from_u32(index); - ComponentItem::Module(index) - } - wasmparser::ComponentExternalKind::Instance => { - let index = ComponentInstanceIndex::from_u32(index); - ComponentItem::ComponentInstance(index) - } - wasmparser::ComponentExternalKind::Component => { - let index = ComponentIndex::from_u32(index); - ComponentItem::Component(index) - } - wasmparser::ComponentExternalKind::Value => { - unimplemented!("component values"); - } - wasmparser::ComponentExternalKind::Type => { - let types = self.validator.types(0).unwrap(); - let ty = types.component_any_type_at(index); - ComponentItem::Type(ty) - } - }) - } - - /// Parses an outer alias to a module or component. - fn alias_component_outer( - &mut self, - kind: wasmparser::ComponentOuterAliasKind, - count: u32, - index: u32, - ) { - match kind { - wasmparser::ComponentOuterAliasKind::CoreType - | wasmparser::ComponentOuterAliasKind::Type => {} - - // For more information about the implementation of outer aliases - // see the documentation of `LexicalScope`. Otherwise though the - // main idea here is that the data to close over starts as `Local` - // and then transitions to `Upvar` as its inserted into the parents - // in order from target we're aliasing back to the current - // component. - wasmparser::ComponentOuterAliasKind::CoreModule => { - let index = ModuleIndex::from_u32(index); - let mut module = ClosedOverModule::Local(index); - let depth = self.lexical_scopes.len() - (count as usize); - for frame in self.lexical_scopes[depth..].iter_mut() { - module = ClosedOverModule::Upvar(frame.closure_args.modules.push(module)); - } - - // If the `module` is still `Local` then the `depth` was 0 and - // it's an alias into our own space. Otherwise it's switched to - // an upvar and will index into the upvar space. Either way - // it's just plumbed directly into the initializer. - self.result - .initializers - .push(LocalInitializer::AliasModule(module)); - } - wasmparser::ComponentOuterAliasKind::Component => { - let index = ComponentIndex::from_u32(index); - let mut component = ClosedOverComponent::Local(index); - let depth = self.lexical_scopes.len() - (count as usize); - for frame in self.lexical_scopes[depth..].iter_mut() { - component = - ClosedOverComponent::Upvar(frame.closure_args.components.push(component)); - } - - self.result - .initializers - .push(LocalInitializer::AliasComponent(component)); - } - } - } - - /// Converts a core wasm function type into our `SignatureIndex`. - fn core_func_signature(&mut self, idx: u32) -> SignatureIndex { - let types = self.validator.types(0).unwrap(); - let id = types.core_function_at(idx); - let ty = types[id].unwrap_func(); - let ty = convert_func_type(ty); - self.types.wasm_func_type(id, ty) - } -} - -/// Parses core module instance -fn instantiate_module<'data>( - module: ModuleIndex, - raw_args: &[wasmparser::InstantiationArg<'data>], -) -> LocalInitializer<'data> { - let mut args = HashMap::with_capacity(raw_args.len()); - for arg in raw_args { - match arg.kind { - wasmparser::InstantiationArgKind::Instance => { - let idx = ModuleInstanceIndex::from_u32(arg.index); - args.insert(arg.name, idx); - } - } - } - LocalInitializer::ModuleInstantiate(module, args) -} - -/// Creates a synthetic module from the list of items currently in the -/// module and their given names. -fn instantiate_module_from_exports<'data>( - exports: &[wasmparser::Export<'data>], -) -> LocalInitializer<'data> { - let mut map = HashMap::with_capacity(exports.len()); - for export in exports { - let idx = match export.kind { - wasmparser::ExternalKind::Func => { - let index = FuncIndex::from_u32(export.index); - EntityIndex::Function(index) - } - wasmparser::ExternalKind::Table => { - let index = TableIndex::from_u32(export.index); - EntityIndex::Table(index) - } - wasmparser::ExternalKind::Memory => { - let index = MemoryIndex::from_u32(export.index); - EntityIndex::Memory(index) - } - wasmparser::ExternalKind::Global => { - let index = GlobalIndex::from_u32(export.index); - EntityIndex::Global(index) - } - - // doesn't get past validation - wasmparser::ExternalKind::Tag => unimplemented!("wasm exceptions"), - }; - map.insert(export.name, idx); - } - LocalInitializer::ModuleSynthetic(map) -} - -/// Converts wasmparser's `CanonicalOption` into our `LocalCanonicalOptions`. -fn canonical_options(opts: &[wasmparser::CanonicalOption]) -> LocalCanonicalOptions { - let mut ret = LocalCanonicalOptions { - string_encoding: StringEncoding::Utf8, - memory: None, - realloc: None, - post_return: None, - }; - for opt in opts { - match opt { - wasmparser::CanonicalOption::UTF8 => { - ret.string_encoding = StringEncoding::Utf8; - } - wasmparser::CanonicalOption::UTF16 => { - ret.string_encoding = StringEncoding::Utf16; - } - wasmparser::CanonicalOption::CompactUTF16 => { - ret.string_encoding = StringEncoding::CompactUtf16; - } - wasmparser::CanonicalOption::Memory(idx) => { - let idx = MemoryIndex::from_u32(*idx); - ret.memory = Some(idx); - } - wasmparser::CanonicalOption::Realloc(idx) => { - let idx = FuncIndex::from_u32(*idx); - ret.realloc = Some(idx); - } - wasmparser::CanonicalOption::PostReturn(idx) => { - let idx = FuncIndex::from_u32(*idx); - ret.post_return = Some(idx); - } - } - } - return ret; -} - -/// Converts wasmparser module instance alias information into `LocalInitializer`. -fn alias_module_instance_export<'data>( - kind: wasmparser::ExternalKind, - instance: ModuleInstanceIndex, - name: &'data str, -) -> LocalInitializer<'data> { - match kind { - wasmparser::ExternalKind::Func => LocalInitializer::AliasExportFunc(instance, name), - wasmparser::ExternalKind::Memory => LocalInitializer::AliasExportMemory(instance, name), - wasmparser::ExternalKind::Table => LocalInitializer::AliasExportTable(instance, name), - wasmparser::ExternalKind::Global => LocalInitializer::AliasExportGlobal(instance, name), - wasmparser::ExternalKind::Tag => { - unimplemented!("wasm exceptions"); - } - } -} - -impl Translation<'_> { - fn types_ref(&self) -> wasmparser::types::TypesRef<'_> { - self.types.as_ref().unwrap().as_ref() - } -} - -#[cfg(test)] -mod tests { - - use crate::test_utils::test_diagnostics; - - use super::*; - - #[test] - fn parse_simple() { - let wat = format!( - r#" -(component - (core module (;0;) - (type (;0;) (func)) - (type (;1;) (func (param i32 i32) (result i32))) - (func $add (;0;) (type 1) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.add - ) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (export "memory" (memory 0)) - (export "add" (func $add)) - ) - (core instance (;0;) (instantiate 0)) - (alias core export 0 "memory" (core memory (;0;))) - (type (;0;) (func (param "a" s32) (param "b" s32) (result s32))) - (alias core export 0 "add" (core func (;0;))) - (func (;0;) (type 0) (canon lift (core func 0))) - (export (;1;) "add" (func 0)) -) - "#, - ); - let wasm = wat::parse_str(wat).unwrap(); - let wasm_features = WasmFeatures::all(); - let diagnostics = test_diagnostics(); - let mut validator = wasmparser::Validator::new_with_features(wasm_features); - let mut types = Default::default(); - let mut translator = - Translator::new(WasmTranslationConfig::default(), &mut validator, &mut types); - translator.parse(&wasm, &diagnostics).unwrap(); - let translation = translator.result; - assert_eq!(translation.exports.len(), 1); - assert_eq!(translation.initializers.len(), 6); - assert_eq!(translator.static_components.len(), 0); - assert_eq!(translator.static_modules.len(), 1); - } -} diff --git a/frontend-wasm/src/component/types.rs b/frontend-wasm/src/component/types.rs deleted file mode 100644 index 9aeff9f3d..000000000 --- a/frontend-wasm/src/component/types.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::hash::Hash; -use wasmparser::types; - -macro_rules! indices { - ($( - $(#[$a:meta])* - pub struct $name:ident(u32); - )*) => ($( - $(#[$a])* - #[derive( - Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, - )] - #[repr(transparent)] - pub struct $name(u32); - miden_hir::cranelift_entity::entity_impl!($name); - )*); -} - -indices! { - // ======================================================================== - // Like Core WebAssembly, the Component Model places each definition into - // one of a fixed set of index spaces, allowing the definition to be - // referred to by subsequent definitions (in the text and binary format) via - // a nonnegative integral index. When defining, validating and executing a - // component, there are 5 component-level index spaces: - - // (component) functions - // (component) values - // (component) types - // component instances - // components - - // and 2 additional core index spaces that contain core definition - // introduced by the Component Model that are not in WebAssembly 1.0 (yet: - // the module-linking proposal would add them): - - // module instances - // modules - - // for a total of 12 index spaces that need to be maintained by an implementation when, e.g., validating a component. - - // These indices are used during compile time only when we're translating a - // component at this time. The actual indices are not persisted beyond the - // compile phase to when we're actually working with the component at - // runtime. - - /// Index within a component's component type index space. - pub struct ComponentTypeIndex(u32); - - /// Index within a component's module index space. - pub struct ModuleIndex(u32); - - /// Index within a component's component index space. - pub struct ComponentIndex(u32); - - /// Index within a component's module instance index space. - pub struct ModuleInstanceIndex(u32); - - /// Index within a component's component instance index space. - pub struct ComponentInstanceIndex(u32); - - /// Index within a component's component function index space. - pub struct ComponentFuncIndex(u32); - - - /// Index into the global list of modules found within an entire component. - /// - /// Module translations are saved on the side to get fully compiled after - /// the original component has finished being translated. - pub struct StaticModuleIndex(u32); - - // ======================================================================== - // Index types used to identify modules and components during compilation. - - /// Index into a "closed over variables" list for components used to - /// implement outer aliases. For more information on this see the - /// documentation for the `LexicalScope` structure. - pub struct ModuleUpvarIndex(u32); - - /// Same as `ModuleUpvarIndex` but for components. - pub struct ComponentUpvarIndex(u32); - - /// Same as `StaticModuleIndex` but for components. - pub struct StaticComponentIndex(u32); - -} - -/// Equivalent of `EntityIndex` but for the component model instead of core -/// wasm. -#[derive(Debug, Clone, Copy)] -pub enum ComponentItem { - Func(ComponentFuncIndex), - Module(ModuleIndex), - Component(ComponentIndex), - ComponentInstance(ComponentInstanceIndex), - Type(types::ComponentAnyTypeId), -} diff --git a/frontend-wasm/src/config.rs b/frontend-wasm/src/config.rs deleted file mode 100644 index d5b6c32f2..000000000 --- a/frontend-wasm/src/config.rs +++ /dev/null @@ -1,22 +0,0 @@ -/// Configuration for the WASM translation. -#[derive(Debug)] -pub struct WasmTranslationConfig { - /// The module name to use if Wasm module doesn't have one. - pub module_name_fallback: String, - - /// Whether or not to generate native DWARF debug information. - pub generate_native_debuginfo: bool, - - /// Whether or not to retain DWARF sections in compiled modules. - pub parse_wasm_debuginfo: bool, -} - -impl Default for WasmTranslationConfig { - fn default() -> Self { - Self { - module_name_fallback: "noname".to_string(), - generate_native_debuginfo: false, - parse_wasm_debuginfo: false, - } - } -} diff --git a/frontend-wasm/src/error.rs b/frontend-wasm/src/error.rs deleted file mode 100644 index 8d7846c81..000000000 --- a/frontend-wasm/src/error.rs +++ /dev/null @@ -1,80 +0,0 @@ -use miden_diagnostics::Diagnostic; -use miden_diagnostics::ToDiagnostic; -use miden_hir::SymbolConflictError; -use thiserror::Error; - -/// A WebAssembly translation 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)] -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}")] - InvalidWebAssembly { - /// A string describing the validation error. - message: String, - /// The bytecode offset where the error occurred. - offset: usize, - }, - - /// A feature used by the WebAssembly code is not supported by the Miden IR. - #[error("Unsupported Wasm: {0}")] - Unsupported(String), - - /// Too many functions were declared in a module - #[error("Too many declared functions in the module")] - FuncNumLimitExceeded, - - /// Duplicate symbol names were found in a module - #[error("{0}")] - 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] miden_hir::LinkerError), -} - -impl From for WasmError { - fn from(e: wasmparser::BinaryReaderError) -> Self { - Self::InvalidWebAssembly { - message: e.message().into(), - offset: e.offset(), - } - } -} - -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; - -/// 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)); - } -} diff --git a/frontend-wasm/src/lib.rs b/frontend-wasm/src/lib.rs deleted file mode 100644 index 92b7372db..000000000 --- a/frontend-wasm/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Performs translation from Wasm to MidenIR - -// Coding conventions -#![deny(warnings)] -#![deny(missing_docs)] -#![deny(rustdoc::broken_intra_doc_links)] -// TODO: remove this once everything is implemented -#![allow(dead_code)] - -mod code_translator; -mod component; -mod config; -mod error; -mod module; -mod ssa; -mod translation_utils; - -#[cfg(test)] -mod test_utils; - -pub use self::component::translate::translate_component; -pub use self::config::WasmTranslationConfig; -pub use self::error::WasmError; -pub use self::module::translate::translate_module; diff --git a/frontend-wasm/src/module/func_translation_state.rs b/frontend-wasm/src/module/func_translation_state.rs deleted file mode 100644 index 67d14dc74..000000000 --- a/frontend-wasm/src/module/func_translation_state.rs +++ /dev/null @@ -1,493 +0,0 @@ -//! WebAssembly module and function translation state. -//! -//! The `FuncTranslationState` struct defined in this module is used to keep track of the WebAssembly -//! value and control stacks during the translation of a single function. -//! -//! Based on Cranelift's Wasm -> CLIF translator v11.0.0 - -use crate::{ - error::{WasmError, WasmResult}, - module::types::{ir_func_type, BlockType, FuncIndex, ModuleTypes}, - translation_utils::sig_from_funct_type, -}; -use miden_diagnostics::{DiagnosticsHandler, SourceSpan}; -use miden_hir::{ - cranelift_entity::EntityRef, Block, CallConv, DataFlowGraph, FunctionIdent, Inst, InstBuilder, - Linkage, Signature, Value, -}; -use miden_hir_type::Type; -use std::{ - collections::{hash_map::Entry::Occupied, hash_map::Entry::Vacant, HashMap}, - vec::Vec, -}; - -use super::{function_builder_ext::FunctionBuilderExt, Module}; - -/// Information about the presence of an associated `else` for an `if`, or the -/// lack thereof. -#[derive(Debug)] -pub enum ElseData { - /// The `if` does not already have an `else` block. - /// - /// This doesn't mean that it will never have an `else`, just that we - /// haven't seen it yet. - NoElse { - /// If we discover that we need an `else` block, this is the jump - /// instruction that needs to be fixed up to point to the new `else` - /// block rather than the destination block after the `if...end`. - branch_inst: Inst, - - /// The placeholder block we're replacing. - placeholder: Block, - }, - - /// We have already allocated an `else` block. - /// - /// Usually we don't know whether we will hit an `if .. end` or an `if - /// .. else .. end`, but sometimes we can tell based on the block's type - /// signature that the signature is not valid if there isn't an `else`. In - /// these cases, we pre-allocate the `else` block. - WithElse { - /// This is the `else` block. - else_block: Block, - }, -} - -/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following -/// fields: -/// -/// - `destination`: reference to the `Block` that will hold the code after the control block; -/// - `num_return_values`: number of values returned by the control block; -/// - `original_stack_size`: size of the value stack at the beginning of the control block. -/// -/// The `loop` frame has a `header` field that references the `Block` that contains the beginning -/// of the body of the loop. -#[derive(Debug)] -pub enum ControlStackFrame { - If { - destination: Block, - else_data: ElseData, - num_param_values: usize, - num_return_values: usize, - original_stack_size: usize, - exit_is_branched_to: bool, - blocktype: BlockType, - /// Was the head of the `if` reachable? - head_is_reachable: bool, - /// What was the reachability at the end of the consequent? - /// - /// This is `None` until we're finished translating the consequent, and - /// is set to `Some` either by hitting an `else` when we will begin - /// translating the alternative, or by hitting an `end` in which case - /// there is no alternative. - consequent_ends_reachable: Option, - // Note: no need for `alternative_ends_reachable` because that is just - // `state.reachable` when we hit the `end` in the `if .. else .. end`. - }, - Block { - destination: Block, - num_param_values: usize, - num_return_values: usize, - original_stack_size: usize, - exit_is_branched_to: bool, - }, - Loop { - destination: Block, - header: Block, - num_param_values: usize, - num_return_values: usize, - original_stack_size: usize, - }, -} - -/// Helper methods for the control stack objects. -impl ControlStackFrame { - pub fn num_return_values(&self) -> usize { - match *self { - Self::If { - num_return_values, .. - } - | Self::Block { - num_return_values, .. - } - | Self::Loop { - num_return_values, .. - } => num_return_values, - } - } - pub fn num_param_values(&self) -> usize { - match *self { - Self::If { - num_param_values, .. - } - | Self::Block { - num_param_values, .. - } - | Self::Loop { - num_param_values, .. - } => num_param_values, - } - } - pub fn following_code(&self) -> Block { - match *self { - Self::If { destination, .. } - | Self::Block { destination, .. } - | Self::Loop { destination, .. } => destination, - } - } - pub fn br_destination(&self) -> Block { - match *self { - Self::If { destination, .. } | Self::Block { destination, .. } => destination, - Self::Loop { header, .. } => header, - } - } - /// Private helper. Use `truncate_value_stack_to_else_params()` or - /// `truncate_value_stack_to_original_size()` to restore value-stack state. - fn original_stack_size(&self) -> usize { - match *self { - Self::If { - original_stack_size, - .. - } - | Self::Block { - original_stack_size, - .. - } - | Self::Loop { - original_stack_size, - .. - } => original_stack_size, - } - } - pub fn is_loop(&self) -> bool { - match *self { - Self::If { .. } | Self::Block { .. } => false, - Self::Loop { .. } => true, - } - } - - pub fn exit_is_branched_to(&self) -> bool { - match *self { - Self::If { - exit_is_branched_to, - .. - } - | Self::Block { - exit_is_branched_to, - .. - } => exit_is_branched_to, - Self::Loop { .. } => false, - } - } - - pub fn set_branched_to_exit(&mut self) { - match *self { - Self::If { - ref mut exit_is_branched_to, - .. - } - | Self::Block { - ref mut exit_is_branched_to, - .. - } => *exit_is_branched_to = true, - Self::Loop { .. } => {} - } - } - - /// Pop values from the value stack so that it is left at the - /// input-parameters to an else-block. - pub fn truncate_value_stack_to_else_params(&self, stack: &mut Vec) { - debug_assert!(matches!(self, &ControlStackFrame::If { .. })); - stack.truncate(self.original_stack_size()); - } - - /// Pop values from the value stack so that it is left at the state it was - /// before this control-flow frame. - pub fn truncate_value_stack_to_original_size(&self, stack: &mut Vec) { - // The "If" frame pushes its parameters twice, so they're available to the else block - // (see also `FuncTranslationState::push_if`). - // Yet, the original_stack_size member accounts for them only once, so that the else - // block can see the same number of parameters as the consequent block. As a matter of - // fact, we need to substract an extra number of parameter values for if blocks. - let num_duplicated_params = match self { - &ControlStackFrame::If { - num_param_values, .. - } => { - debug_assert!(num_param_values <= self.original_stack_size()); - num_param_values - } - _ => 0, - }; - stack.truncate(self.original_stack_size() - num_duplicated_params); - } -} - -/// Contains information passed along during a function's translation and that records: -/// -/// - The current value and control stacks. -/// - The depth of the two unreachable control blocks stacks, that are manipulated when translating -/// unreachable code; -pub struct FuncTranslationState { - /// A stack of values corresponding to the active values in the input wasm function at this - /// point. - pub(crate) stack: Vec, - /// A stack of active control flow operations at this point in the input wasm function. - pub(crate) control_stack: Vec, - /// Is the current translation state still reachable? This is false when translating operators - /// like End, Return, or Unreachable. - pub(crate) reachable: bool, - - // Imported and local functions that have been created by - // `FuncEnvironment::make_direct_func()`. - // Stores both the function reference and the number of WebAssembly arguments - functions: HashMap, -} - -impl FuncTranslationState { - /// Construct a new, empty, `FuncTranslationState` - pub(crate) fn new() -> Self { - Self { - stack: Vec::new(), - control_stack: Vec::new(), - reachable: true, - functions: HashMap::new(), - } - } - - fn clear(&mut self) { - debug_assert!(self.stack.is_empty()); - debug_assert!(self.control_stack.is_empty()); - self.reachable = true; - self.functions.clear(); - } - - /// Initialize the state for compiling a function with the given signature. - /// - /// This resets the state to containing only a single block representing the whole function. - /// The exit block is the last block in the function which will contain the return instruction. - pub(crate) fn initialize(&mut self, sig: &Signature, exit_block: Block) { - self.clear(); - self.push_block(exit_block, 0, sig.results().len()); - } - - /// Push a value. - pub(crate) fn push1(&mut self, val: Value) { - self.stack.push(val); - } - - /// Push multiple values. - pub(crate) fn pushn(&mut self, vals: &[Value]) { - self.stack.extend_from_slice(vals); - } - - /// Pop one value. - pub(crate) fn pop1(&mut self) -> Value { - self.stack - .pop() - .expect("attempted to pop a value from an empty stack") - } - - /// Pop one value and cast it to the specified type. - pub(crate) fn pop1_casted( - &mut self, - ty: Type, - builder: &mut FunctionBuilderExt, - span: SourceSpan, - ) -> Value { - let val = self - .stack - .pop() - .expect("attempted to pop a value from an empty stack"); - builder.ins().cast(val, ty.clone(), span) - } - - /// Peek at the top of the stack without popping it. - pub(crate) fn peek1(&self) -> Value { - *self - .stack - .last() - .expect("attempted to peek at a value on an empty stack") - } - - /// Pop two values. Return them in the order they were pushed. - pub(crate) fn pop2(&mut self) -> (Value, Value) { - let v2 = self.stack.pop().unwrap(); - let v1 = self.stack.pop().unwrap(); - (v1, v2) - } - - /// Pop two values. Cast them to the specified type. Return them in the order they were pushed. - pub(crate) fn pop2_casted( - &mut self, - ty: Type, - builder: &mut FunctionBuilderExt, - span: SourceSpan, - ) -> (Value, Value) { - let v2 = self.stack.pop().unwrap(); - let v1 = self.stack.pop().unwrap(); - let v1_casted = builder.ins().cast(v1, ty.clone(), span); - let v2_casted = builder.ins().cast(v2, ty, span); - (v1_casted, v2_casted) - } - - /// Pop three values. Return them in the order they were pushed. - pub(crate) fn pop3(&mut self) -> (Value, Value, Value) { - let v3 = self.stack.pop().unwrap(); - let v2 = self.stack.pop().unwrap(); - let v1 = self.stack.pop().unwrap(); - (v1, v2, v3) - } - - /// Helper to ensure the the stack size is at least as big as `n`; note that due to - /// `debug_assert` this will not execute in non-optimized builds. - #[inline] - fn ensure_length_is_at_least(&self, n: usize) { - debug_assert!( - n <= self.stack.len(), - "attempted to access {} values but stack only has {} values", - n, - self.stack.len() - ) - } - - /// Pop the top `n` values on the stack. - /// - /// The popped values are not returned. Use `peekn` to look at them before popping. - pub(crate) fn popn(&mut self, n: usize) { - self.ensure_length_is_at_least(n); - let new_len = self.stack.len() - n; - self.stack.truncate(new_len); - } - - /// Peek at the top `n` values on the stack in the order they were pushed. - pub(crate) fn peekn(&self, n: usize) -> &[Value] { - self.ensure_length_is_at_least(n); - &self.stack[self.stack.len() - n..] - } - - /// Peek at the top `n` values on the stack in the order they were pushed. - pub(crate) fn peekn_mut(&mut self, n: usize) -> &mut [Value] { - self.ensure_length_is_at_least(n); - let len = self.stack.len(); - &mut self.stack[len - n..] - } - - /// Push a block on the control stack. - pub(crate) fn push_block( - &mut self, - following_code: Block, - num_param_types: usize, - num_result_types: usize, - ) { - debug_assert!(num_param_types <= self.stack.len()); - self.control_stack.push(ControlStackFrame::Block { - destination: following_code, - original_stack_size: self.stack.len() - num_param_types, - num_param_values: num_param_types, - num_return_values: num_result_types, - exit_is_branched_to: false, - }); - } - - /// Push a loop on the control stack. - pub(crate) fn push_loop( - &mut self, - header: Block, - following_code: Block, - num_param_types: usize, - num_result_types: usize, - ) { - debug_assert!(num_param_types <= self.stack.len()); - self.control_stack.push(ControlStackFrame::Loop { - header, - destination: following_code, - original_stack_size: self.stack.len() - num_param_types, - num_param_values: num_param_types, - num_return_values: num_result_types, - }); - } - - /// Push an if on the control stack. - pub(crate) fn push_if( - &mut self, - destination: Block, - else_data: ElseData, - num_param_types: usize, - num_result_types: usize, - blocktype: BlockType, - ) { - debug_assert!(num_param_types <= self.stack.len()); - - // Push a second copy of our `if`'s parameters on the stack. This lets - // us avoid saving them on the side in the `ControlStackFrame` for our - // `else` block (if it exists), which would require a second heap - // allocation. See also the comment in `translate_operator` for - // `Operator::Else`. - self.stack.reserve(num_param_types); - for i in (self.stack.len() - num_param_types)..self.stack.len() { - let val = self.stack[i]; - self.stack.push(val); - } - - self.control_stack.push(ControlStackFrame::If { - destination, - else_data, - original_stack_size: self.stack.len() - num_param_types, - num_param_values: num_param_types, - num_return_values: num_result_types, - exit_is_branched_to: false, - head_is_reachable: self.reachable, - consequent_ends_reachable: None, - blocktype, - }); - } -} - -/// Methods for handling entity references. -impl FuncTranslationState { - /// Get the `FunctionIdent` that should be used to make a direct call to function - /// `index`. Also return the number of WebAssembly arguments in the signature. - /// - /// Import the callee into `func`'s DFG if it is not already present. - pub(crate) fn get_direct_func( - &mut self, - dfg: &mut DataFlowGraph, - index: u32, - mod_info: &Module, - mod_types: &ModuleTypes, - diagnostics: &DiagnosticsHandler, - ) -> WasmResult<(FunctionIdent, usize)> { - let index = FuncIndex::from_u32(index); - Ok(match self.functions.entry(index) { - Occupied(entry) => *entry.get(), - Vacant(entry) => { - let func_type_idx = mod_info.functions[index].clone(); - let func_type = mod_types[func_type_idx.signature].clone(); - let func_name = mod_info - .name_section - .func_names - .get(&index) - .cloned() - .unwrap_or_else(|| format!("func{}", index.index())); - let mod_name = mod_info - .name_section - .module_name - .clone() - .expect("Module name should be set by this point"); - let mod_ident = mod_name.as_str().into(); - let func_name_id = func_name.as_str().into(); - let ir_func_type = ir_func_type(&func_type)?; - let sig = sig_from_funct_type(&ir_func_type, CallConv::SystemV, Linkage::External); - let Ok(func_id) = dfg.import_function(mod_ident, func_name_id, sig.clone()) else { - let message = format!("Function with name {} in module {} with signature {sig:?} is already imported (function call) with a different signature", func_name_id, mod_ident); - diagnostics - .diagnostic(miden_diagnostics::Severity::Error) - .with_message(message.clone()) - .emit(); - return Err(WasmError::Unexpected(message)); - }; - *entry.insert((func_id, sig.params().len())) - } - }) - } -} diff --git a/frontend-wasm/src/module/func_translator.rs b/frontend-wasm/src/module/func_translator.rs deleted file mode 100644 index b6021bf57..000000000 --- a/frontend-wasm/src/module/func_translator.rs +++ /dev/null @@ -1,195 +0,0 @@ -//! Stand-alone WebAssembly to Miden IR translator. -//! -//! This module defines the `FuncTranslator` type which can translate a single WebAssembly -//! function to Miden IR guided by a `FuncEnvironment` which provides information about the -//! WebAssembly module and the runtime environment. -//! -//! Based on Cranelift's Wasm -> CLIF translator v11.0.0 - -use crate::code_translator::translate_operator; -use crate::error::WasmResult; -use crate::module::func_translation_state::FuncTranslationState; -use crate::module::function_builder_ext::{FunctionBuilderContext, FunctionBuilderExt}; -use crate::module::types::{convert_valtype, ir_type, ModuleTypes}; -use crate::ssa::Variable; -use crate::translation_utils::emit_zero; -use miden_diagnostics::{DiagnosticsHandler, SourceSpan}; -use miden_hir::cranelift_entity::EntityRef; -use miden_hir::{Block, InstBuilder, ModuleFunctionBuilder}; -use wasmparser::{BinaryReader, FuncValidator, FunctionBody, WasmModuleResources}; - -use super::Module; - -/// WebAssembly to Miden IR function translator. -/// -/// A `FuncTranslator` is used to translate a binary WebAssembly function into Miden IR guided -/// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple -/// functions which will reduce heap allocation traffic. -pub struct FuncTranslator { - func_ctx: FunctionBuilderContext, - state: FuncTranslationState, -} - -impl FuncTranslator { - /// Create a new translator. - pub fn new() -> Self { - Self { - func_ctx: FunctionBuilderContext::new(), - state: FuncTranslationState::new(), - } - } - - /// Translate a binary WebAssembly function from a `FunctionBody`. - pub fn translate_body( - &mut self, - body: &FunctionBody<'_>, - mod_func_builder: &mut ModuleFunctionBuilder, - mod_info: &Module, - mod_types: &ModuleTypes, - diagnostics: &DiagnosticsHandler, - 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. - - let num_params = declare_parameters(&mut builder, entry_block); - - // Set up the translation state with a single pushed control block representing the whole - // function and its return values. - let exit_block = builder.create_block(); - 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)?; - parse_function_body( - reader, - &mut builder, - &mut self.state, - mod_info, - mod_types, - diagnostics, - func_validator, - )?; - - builder.finalize(); - Ok(()) - } -} - -/// Declare local variables for the signature parameters that correspond to WebAssembly locals. -/// -/// Return the number of local variables declared. -fn declare_parameters(builder: &mut FunctionBuilderExt, entry_block: Block) -> usize { - let sig_len = builder.signature().params().len(); - let mut next_local = 0; - for i in 0..sig_len { - let abi_param = &builder.signature().params()[i]; - let local = Variable::new(next_local); - builder.declare_var(local, abi_param.ty.clone()); - next_local += 1; - - let param_value = builder.block_params(entry_block)[i]; - builder.def_var(local, param_value); - } - next_local -} - -/// Parse the local variable declarations that precede the function body. -/// -/// Declare local variables, starting from `num_params`. -fn parse_local_decls( - reader: &mut BinaryReader, - builder: &mut FunctionBuilderExt, - num_params: usize, - validator: &mut FuncValidator, -) -> WasmResult<()> { - let mut next_local = num_params; - let local_count = reader.read_var_u32()?; - - 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)?; - } - - Ok(()) -} - -/// Declare `count` local variables of the same type, starting from `next_local`. -/// -/// Fail if too many locals are declared in the function, or if the type is not valid for a local. -fn declare_locals( - builder: &mut FunctionBuilderExt, - count: u32, - wasm_type: wasmparser::ValType, - next_local: &mut usize, -) -> WasmResult<()> { - let ty = ir_type(convert_valtype(wasm_type))?; - // All locals are initialized to 0. - let init = emit_zero(&ty, builder)?; - for _ in 0..count { - let local = Variable::new(*next_local); - builder.declare_var(local, ty.clone()); - builder.def_var(local, init); - *next_local += 1; - } - Ok(()) -} - -/// Parse the function body in `reader`. -/// -/// This assumes that the local variable declarations have already been parsed and function -/// arguments and locals are declared in the builder. -fn parse_function_body( - mut reader: BinaryReader, - builder: &mut FunctionBuilderExt, - state: &mut FuncTranslationState, - mod_info: &Module, - mod_types: &ModuleTypes, - diagnostics: &DiagnosticsHandler, - 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"); - - while !reader.eof() { - let pos = reader.original_position(); - let op = reader.read_operator()?; - func_validator.op(pos, &op)?; - translate_operator( - &op, - builder, - state, - mod_info, - mod_types, - diagnostics, - SourceSpan::default(), - )?; - } - let pos = reader.original_position(); - func_validator.finish(pos)?; - - // The final `End` operator left us in the exit block where we need to manually add a return - // instruction. - // - // 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 { - if !builder.is_unreachable() { - builder - .ins() - .ret(state.stack.first().cloned(), SourceSpan::default()); - } - } - - // Discard any remaining values on the stack. Either we just returned them, - // or the end of the function is unreachable. - state.stack.clear(); - - Ok(()) -} diff --git a/frontend-wasm/src/module/function_builder_ext.rs b/frontend-wasm/src/module/function_builder_ext.rs deleted file mode 100644 index 2358ea420..000000000 --- a/frontend-wasm/src/module/function_builder_ext.rs +++ /dev/null @@ -1,525 +0,0 @@ -use miden_diagnostics::SourceSpan; -use miden_hir::cranelift_entity::EntitySet; -use miden_hir::cranelift_entity::SecondaryMap; -use miden_hir::Block; -use miden_hir::Br; -use miden_hir::CondBr; -use miden_hir::DataFlowGraph; -use miden_hir::InsertionPoint; -use miden_hir::Inst; -use miden_hir::InstBuilderBase; -use miden_hir::Instruction; -use miden_hir::ModuleFunctionBuilder; -use miden_hir::ProgramPoint; -use miden_hir::Switch; -use miden_hir::Value; -use miden_hir_type::Type; - -use crate::ssa::SSABuilder; -use crate::ssa::SideEffects; -use crate::ssa::Variable; - -/// Tracking variables and blocks for SSA construction. -pub struct FunctionBuilderContext { - ssa: SSABuilder, - status: SecondaryMap, - types: SecondaryMap, -} - -impl FunctionBuilderContext { - pub fn new() -> Self { - Self { - ssa: SSABuilder::default(), - status: SecondaryMap::new(), - types: SecondaryMap::with_default(Type::Unknown), - } - } - - fn is_empty(&self) -> bool { - self.ssa.is_empty() && self.status.is_empty() && self.types.is_empty() - } - - fn clear(&mut self) { - self.ssa.clear(); - self.status.clear(); - self.types.clear(); - } -} - -#[derive(Clone, Default, Eq, PartialEq)] -enum BlockStatus { - /// No instructions have been added. - #[default] - Empty, - /// Some instructions have been added, but no terminator. - Partial, - /// A terminator has been added; no further instructions may be added. - Filled, -} - -/// A wrapper around Miden's `FunctionBuilder` and `SSABuilder` which provides -/// additional API for dealing with variables and SSA construction. -pub struct FunctionBuilderExt<'a, 'b, 'c: 'b> { - inner: &'b mut ModuleFunctionBuilder<'c>, - func_ctx: &'a mut FunctionBuilderContext, -} - -impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { - pub fn new( - inner: &'b mut ModuleFunctionBuilder<'c>, - func_ctx: &'a mut FunctionBuilderContext, - ) -> Self { - debug_assert!(func_ctx.is_empty()); - Self { inner, func_ctx } - } - - pub fn data_flow_graph(&self) -> &DataFlowGraph { - &self.inner.data_flow_graph() - } - - pub fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { - self.inner.data_flow_graph_mut() - } - - pub fn signature(&self) -> &miden_hir::Signature { - self.inner.signature() - } - - pub fn ins<'short>(&'short mut self) -> FuncInstBuilderExt<'short, 'a, 'b, 'c> { - let block = self.inner.current_block(); - FuncInstBuilderExt::new(self, block) - } - - #[inline] - pub fn current_block(&self) -> Block { - self.inner.current_block() - } - - pub fn inst_results(&self, inst: Inst) -> &[Value] { - self.inner.inst_results(inst) - } - - pub fn create_block(&mut self) -> Block { - let block = self.inner.create_block(); - self.func_ctx.ssa.declare_block(block); - block - } - - /// Create a `Block` with the given parameters. - pub fn create_block_with_params( - &mut self, - params: impl IntoIterator, - span: SourceSpan, - ) -> Block { - let block = self.create_block(); - for ty in params { - self.inner.append_block_param(block, ty, span); - } - block - } - - /// Append parameters to the given `Block` corresponding to the function - /// return values. This can be used to set up the block parameters for a - /// function exit block. - pub fn append_block_params_for_function_returns(&mut self, block: Block) { - // These parameters count as "user" parameters here because they aren't - // inserted by the SSABuilder. - debug_assert!( - self.is_pristine(block), - "You can't add block parameters after adding any instruction" - ); - - for argtyp in self.signature().results().to_vec() { - self.inner - .append_block_param(block, argtyp.ty.clone(), SourceSpan::default()); - } - } - - /// After the call to this function, new instructions will be inserted into the designated - /// block, in the order they are declared. You must declare the types of the Block arguments - /// you will use here. - /// - /// When inserting the terminator instruction (which doesn't have a fallthrough to its immediate - /// successor), the block will be declared filled and it will not be possible to append - /// instructions to it. - pub fn switch_to_block(&mut self, block: Block) { - // First we check that the previous block has been filled. - debug_assert!( - self.is_unreachable() - || self.is_pristine(self.inner.current_block()) - || self.is_filled(self.inner.current_block()), - "you have to fill your block before switching" - ); - // We cannot switch to a filled block - debug_assert!( - !self.is_filled(block), - "you cannot switch to a block which is already filled" - ); - // Then we change the cursor position. - self.inner.switch_to_block(block); - } - - /// Retrieves all the parameters for a `Block` currently inferred from the jump instructions - /// inserted that target it and the SSA construction. - pub fn block_params(&self, block: Block) -> &[Value] { - self.inner.block_params(block) - } - - /// Declares that all the predecessors of this block are known. - /// - /// Function to call with `block` as soon as the last branch instruction to `block` has been - /// created. Forgetting to call this method on every block will cause inconsistencies in the - /// produced functions. - pub fn seal_block(&mut self, block: Block) { - let side_effects = self - .func_ctx - .ssa - .seal_block(block, self.inner.data_flow_graph_mut()); - self.handle_ssa_side_effects(side_effects); - } - - /// A Block is 'filled' when a terminator instruction is present. - fn fill_current_block(&mut self) { - self.func_ctx.status[self.inner.current_block()] = BlockStatus::Filled; - } - - fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) { - for modified_block in side_effects.instructions_added_to_blocks { - if self.is_pristine(modified_block) { - self.func_ctx.status[modified_block] = BlockStatus::Partial; - } - } - } - - /// Make sure that the current block is inserted in the layout. - pub fn ensure_inserted_block(&mut self) { - let block = self.inner.current_block(); - if self.is_pristine(block) { - self.func_ctx.status[block] = BlockStatus::Partial; - } else { - debug_assert!( - !self.is_filled(block), - "you cannot add an instruction to a block already filled" - ); - } - } - - /// Declare that translation of the current function is complete. - /// - /// This resets the state of the `FunctionBuilderContext` in preparation to - /// be used for another function. - pub fn finalize(self) { - // Check that all the `Block`s are filled and sealed. - #[cfg(debug_assertions)] - { - for block in self.func_ctx.status.keys() { - if !self.is_pristine(block) { - assert!( - self.func_ctx.ssa.is_sealed(block), - "FunctionBuilderExt finalized, but block {} is not sealed", - block, - ); - assert!( - self.is_filled(block), - "FunctionBuilderExt finalized, but block {} is not filled", - block, - ); - } - } - } - - // Clear the state (but preserve the allocated buffers) in preparation - // for translation another function. - self.func_ctx.clear(); - } - - /// Declares the type of a variable, so that it can be used later (by calling - /// [`FunctionBuilderExt::use_var`]). This function will return an error if the variable - /// has been previously declared. - pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> { - if self.func_ctx.types[var] != Type::Unknown { - return Err(DeclareVariableError::DeclaredMultipleTimes(var)); - } - self.func_ctx.types[var] = ty; - Ok(()) - } - - /// In order to use a variable (by calling [`FunctionBuilderExt::use_var`]), you need - /// to first declare its type with this method. - pub fn declare_var(&mut self, var: Variable, ty: Type) { - self.try_declare_var(var, ty) - .unwrap_or_else(|_| panic!("the variable {:?} has been declared multiple times", var)) - } - - /// Returns the Miden IR necessary to use a previously defined user - /// variable, returning an error if this is not possible. - pub fn try_use_var(&mut self, var: Variable) -> Result { - // Assert that we're about to add instructions to this block using the definition of the - // given variable. ssa.use_var is the only part of this crate which can add block parameters - // behind the caller's back. If we disallow calling append_block_param as soon as use_var is - // called, then we enforce a strict separation between user parameters and SSA parameters. - self.ensure_inserted_block(); - - let (val, side_effects) = { - let ty = self - .func_ctx - .types - .get(var) - .cloned() - .ok_or(UseVariableError::UsedBeforeDeclared(var))?; - debug_assert_ne!( - ty, - Type::Unknown, - "variable {:?} is used but its type has not been declared", - var - ); - let current_block = self.inner.current_block(); - self.func_ctx - .ssa - .use_var(self.inner.data_flow_graph_mut(), var, ty, current_block) - }; - self.handle_ssa_side_effects(side_effects); - Ok(val) - } - - /// Returns the Miden IR value corresponding to the utilization at the current program - /// position of a previously defined user variable. - pub fn use_var(&mut self, var: Variable) -> Value { - self.try_use_var(var).unwrap_or_else(|_| { - panic!( - "variable {:?} is used but its type has not been declared", - var - ) - }) - } - - /// Registers a new definition of a user variable. This function will return - /// an error if the value supplied does not match the type the variable was - /// declared to have. - pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> { - let var_ty = self - .func_ctx - .types - .get(var) - .ok_or(DefVariableError::DefinedBeforeDeclared(var))?; - if var_ty != self.data_flow_graph().value_type(val) { - return Err(DefVariableError::TypeMismatch(var, val)); - } - - self.func_ctx - .ssa - .def_var(var, val, self.inner.current_block()); - Ok(()) - } - - /// Register a new definition of a user variable. The type of the value must be - /// the same as the type registered for the variable. - pub fn def_var(&mut self, var: Variable, val: Value) { - self.try_def_var(var, val) - .unwrap_or_else(|error| match error { - DefVariableError::TypeMismatch(var, val) => { - assert_eq!( - &self.func_ctx.types[var], - self.data_flow_graph().value_type(val), - "declared type of variable {:?} doesn't match type of value {}", - var, - val - ); - } - DefVariableError::DefinedBeforeDeclared(var) => { - panic!( - "variable {:?} is used but its type has not been declared", - var - ); - } - }) - } - - /// Returns `true` if and only if no instructions have been added since the last call to - /// `switch_to_block`. - fn is_pristine(&self, block: Block) -> bool { - self.func_ctx.status[block] == BlockStatus::Empty - } - - /// Returns `true` if and only if a terminator instruction has been inserted since the - /// last call to `switch_to_block`. - fn is_filled(&self, block: Block) -> bool { - self.func_ctx.status[block] == BlockStatus::Filled - } - - /// Returns `true` if and only if the current `Block` is sealed and has no predecessors declared. - /// - /// The entry block of a function is never unreachable. - pub fn is_unreachable(&self) -> bool { - let is_entry = self.inner.current_block() == self.data_flow_graph().entry_block(); - !is_entry - && self.func_ctx.ssa.is_sealed(self.inner.current_block()) - && !self - .func_ctx - .ssa - .has_any_predecessors(self.inner.current_block()) - } - - /// Changes the destination of a jump instruction after creation. - /// - /// **Note:** You are responsible for maintaining the coherence with the arguments of - /// 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 { - Instruction::Br(Br { - ref mut destination, - .. - }) if destination == &old_block => { - *destination = new_block; - } - Instruction::CondBr(CondBr { - then_dest: (ref mut then_dest, _), - else_dest: (ref mut else_dest, _), - .. - }) => { - if then_dest == &old_block { - *then_dest = new_block; - } else if else_dest == &old_block { - *else_dest = new_block; - } - } - Instruction::Switch(Switch { - op: _, - arg: _, - ref mut arms, - ref mut default, - }) => { - for (_, ref mut dest_block) in arms { - if dest_block == &old_block { - *dest_block = new_block; - } - } - if default == &old_block { - *default = new_block; - } - } - _ => panic!("{} must be a branch instruction", inst), - } - self.func_ctx.ssa.declare_block_predecessor(new_block, inst); - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] -/// An error encountered when calling [`FunctionBuilderExt::try_use_var`]. -pub enum UseVariableError { - #[error("variable {0} is used before the declaration")] - UsedBeforeDeclared(Variable), -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)] -/// An error encountered when calling [`FunctionBuilderExt::try_declare_var`]. -pub enum DeclareVariableError { - #[error("variable {0} is already declared")] - DeclaredMultipleTimes(Variable), -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)] -/// An error encountered when defining the initial value of a variable. -pub enum DefVariableError { - #[error("the types of variable {0} and value {1} are not the same. The `Value` supplied to `def_var` must be of the same type as the variable was declared to be of in `declare_var`.")] - TypeMismatch(Variable, Value), - #[error( - "the value of variable {0} was defined (in call `def_val`) before it was declared (in call `declare_var`)" - )] - DefinedBeforeDeclared(Variable), -} - -pub struct FuncInstBuilderExt<'a, 'b: 'a, 'c, 'd: 'c> { - builder: &'a mut FunctionBuilderExt<'b, 'c, 'd>, - ip: InsertionPoint, -} -impl<'a, 'b, 'c, 'd> FuncInstBuilderExt<'a, 'b, 'c, 'd> { - fn new(builder: &'a mut FunctionBuilderExt<'b, 'c, 'd>, block: Block) -> Self { - assert!(builder.data_flow_graph().is_block_linked(block)); - Self { - builder, - ip: InsertionPoint::after(ProgramPoint::Block(block)), - } - } -} -impl<'a, 'b, 'c, 'd> InstBuilderBase<'a> for FuncInstBuilderExt<'a, 'b, 'c, 'd> { - fn data_flow_graph(&self) -> &DataFlowGraph { - &self.builder.data_flow_graph() - } - - fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { - self.builder.data_flow_graph_mut() - } - - fn insertion_point(&self) -> InsertionPoint { - self.ip - } - - // This implementation is richer than `InsertBuilder` because we use the data of the - // instruction being inserted to add related info to the DFG and the SSA building system, - // and perform debug sanity checks. - fn build(self, data: Instruction, ty: Type, span: SourceSpan) -> (Inst, &'a mut DataFlowGraph) { - // We only insert the Block in the layout when an instruction is added to it - self.builder.ensure_inserted_block(); - 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, .. }) => { - // 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); - } - - Instruction::CondBr(CondBr { - then_dest: (block_then, _), - else_dest: (block_else, _), - .. - }) => { - 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); - } - } - Instruction::Switch(Switch { - op: _, - arg: _, - ref arms, - default: _, - }) => { - // 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) { - continue; - } - self.builder - .func_ctx - .ssa - .declare_block_predecessor(*dest_block, inst); - } - } - inst => debug_assert!(!inst.opcode().is_branch()), - } - - if opcode.is_terminator() { - self.builder.fill_current_block() - } - (inst, self.builder.data_flow_graph_mut()) - } -} diff --git a/frontend-wasm/src/module/mod.rs b/frontend-wasm/src/module/mod.rs deleted file mode 100644 index efe845a58..000000000 --- a/frontend-wasm/src/module/mod.rs +++ /dev/null @@ -1,366 +0,0 @@ -//! Data structures for representing parsed Wasm modules. - -use crate::error::WasmResult; -use crate::unsupported_diag; - -use self::types::*; - -use indexmap::IndexMap; -use miden_diagnostics::DiagnosticsHandler; -use miden_hir::cranelift_entity::packed_option::ReservedValue; -use miden_hir::cranelift_entity::{EntityRef, PrimaryMap}; -use std::collections::{BTreeMap, HashMap}; - -use std::ops::Range; - -pub mod func_translation_state; -pub mod func_translator; -pub mod function_builder_ext; -pub mod module_env; -pub mod translate; -pub mod types; - -/// Table initialization data for all tables in the module. -#[derive(Debug, Default)] -pub struct TableInitialization { - /// Initial values for tables defined within the module itself. - /// - /// This contains the initial values and initializers for tables defined - /// within a wasm, so excluding imported tables. This initializer can - /// represent null-initialized tables, element-initialized tables (e.g. with - /// the function-references proposal), or precomputed images of table - /// initialization. For example table initializers to a table that are all - /// in-bounds will get removed from `segment` and moved into - /// `initial_values` here. - pub initial_values: PrimaryMap, - - /// Element segments present in the initial wasm module which are executed - /// at instantiation time. - /// - /// These element segments are iterated over during instantiation to apply - /// any segments that weren't already moved into `initial_values` above. - pub segments: Vec, -} - -/// Initial value for all elements in a table. -#[derive(Clone, Debug)] -pub enum TableInitialValue { - /// Initialize each table element to null, optionally setting some elements - /// to non-null given the precomputed image. - Null { - /// A precomputed image of table initializers for this table. - precomputed: Vec, - }, - - /// Initialize each table element to the function reference given - /// by the `FuncIndex`. - FuncRef(FuncIndex), -} - -/// A WebAssembly table initializer segment. -#[derive(Clone, Debug)] -pub struct TableSegment { - /// The index of a table to initialize. - pub table_index: TableIndex, - /// Optionally, a global variable giving a base index. - pub base: Option, - /// The offset to add to the base. - pub offset: u32, - /// The values to write into the table elements. - pub elements: Box<[FuncIndex]>, -} - -/// Different types that can appear in a module. -/// -/// Note that each of these variants are intended to index further into a -/// separate table. -#[derive(Debug, Copy, Clone)] -pub enum ModuleType { - Function(SignatureIndex), -} - -impl ModuleType { - /// Asserts this is a `ModuleType::Function`, returning the underlying - /// `SignatureIndex`. - pub fn unwrap_function(&self) -> SignatureIndex { - match self { - ModuleType::Function(f) => *f, - } - } -} - -/// A translated WebAssembly module, excluding the function bodies -#[derive(Default, Debug)] -pub struct Module { - /// All import records, in the order they are declared in the module. - pub initializers: Vec, - - /// Exported entities. - pub exports: IndexMap, - - /// The module "start" function, if present. - pub start_func: Option, - - /// WebAssembly table initialization data, per table. - pub table_initialization: TableInitialization, - - /// WebAssembly passive elements. - pub passive_elements: Vec>, - - /// The map from passive element index (element segment index space) to index in `passive_elements`. - pub passive_elements_map: BTreeMap, - - /// The map from passive data index (data segment index space) to index in `passive_data`. - pub passive_data_map: BTreeMap>, - - /// Types declared in the wasm module. - pub types: PrimaryMap, - - /// Number of imported or aliased functions in the module. - pub num_imported_funcs: usize, - - /// Number of imported or aliased tables in the module. - pub num_imported_tables: usize, - - /// Number of imported or aliased memories in the module. - pub num_imported_memories: usize, - - /// Number of imported or aliased globals in the module. - pub num_imported_globals: usize, - - /// Number of functions that "escape" from this module - /// - /// This is also the number of functions in the `functions` array below with - /// an `func_ref` index (and is the maximum func_ref index). - pub num_escaped_funcs: usize, - - /// Types of functions, imported and local. - pub functions: PrimaryMap, - - /// WebAssembly tables. - pub tables: PrimaryMap, - - /// WebAssembly global variables. - pub globals: PrimaryMap, - - /// WebAssembly global initializers for locally-defined globals. - pub global_initializers: PrimaryMap, - - /// WebAssembly module memories. - pub memories: PrimaryMap, - - /// Parsed names section. - pub name_section: NameSection, -} - -/// Initialization routines for creating an instance, encompassing imports, -/// modules, instances, aliases, etc. -#[derive(Debug)] -pub enum Initializer { - /// An imported item is required to be provided. - Import { - /// Name of this import - name: String, - /// The field name projection of this import - field: String, - /// Where this import will be placed, which also has type information - /// about the import. - index: EntityIndex, - }, -} - -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 { - FuncIndex::new(self.num_imported_funcs + defined_func.index()) - } - - /// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the - /// index is an imported function. - #[inline] - pub fn defined_func_index(&self, func: FuncIndex) -> Option { - if func.index() < self.num_imported_funcs { - None - } else { - Some(DefinedFuncIndex::new( - func.index() - self.num_imported_funcs, - )) - } - } - - /// Test whether the given function index is for an imported function. - #[inline] - pub fn is_imported_function(&self, index: FuncIndex) -> bool { - index.index() < self.num_imported_funcs - } - - /// Convert a `DefinedTableIndex` into a `TableIndex`. - #[inline] - pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex { - TableIndex::new(self.num_imported_tables + defined_table.index()) - } - - /// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the - /// index is an imported table. - #[inline] - pub fn defined_table_index(&self, table: TableIndex) -> Option { - if table.index() < self.num_imported_tables { - None - } else { - Some(DefinedTableIndex::new( - table.index() - self.num_imported_tables, - )) - } - } - - /// Test whether the given table index is for an imported table. - #[inline] - pub fn is_imported_table(&self, index: TableIndex) -> bool { - index.index() < self.num_imported_tables - } - - /// Convert a `DefinedMemoryIndex` into a `MemoryIndex`. - #[inline] - pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex { - MemoryIndex::new(self.num_imported_memories + defined_memory.index()) - } - - /// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the - /// index is an imported memory. - #[inline] - pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option { - if memory.index() < self.num_imported_memories { - None - } else { - Some(DefinedMemoryIndex::new( - memory.index() - self.num_imported_memories, - )) - } - } - - /// Test whether the given memory index is for an imported memory. - #[inline] - pub fn is_imported_memory(&self, index: MemoryIndex) -> bool { - index.index() < self.num_imported_memories - } - - /// Convert a `DefinedGlobalIndex` into a `GlobalIndex`. - #[inline] - pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex { - GlobalIndex::new(self.num_imported_globals + defined_global.index()) - } - - /// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the - /// index is an imported global. - #[inline] - pub fn defined_global_index(&self, global: GlobalIndex) -> Option { - if global.index() < self.num_imported_globals { - None - } else { - Some(DefinedGlobalIndex::new( - global.index() - self.num_imported_globals, - )) - } - } - - /// Test whether the given global index is for an imported global. - #[inline] - pub fn is_imported_global(&self, index: GlobalIndex) -> bool { - index.index() < self.num_imported_globals - } - - /// Returns an iterator of all the imports in this module, along with their - /// module name, field name, and type that's being imported. - pub fn imports(&self) -> impl ExactSizeIterator { - self.initializers.iter().map(move |i| match i { - Initializer::Import { name, field, index } => { - (name.as_str(), field.as_str(), self.type_of(*index)) - } - }) - } - - /// Returns the type of an item based on its index - pub fn type_of(&self, index: EntityIndex) -> EntityType { - match index { - EntityIndex::Global(i) => EntityType::Global(self.globals[i].clone()), - EntityIndex::Table(i) => EntityType::Table(self.tables[i]), - EntityIndex::Memory(i) => EntityType::Memory(self.memories[i]), - EntityIndex::Function(i) => EntityType::Function(self.functions[i].signature), - } - } - - /// Appends a new function to this module with the given type information, - /// used for functions that either don't escape or aren't certain whether - /// they escape yet. - pub fn push_function(&mut self, signature: SignatureIndex) -> FuncIndex { - self.functions.push(FunctionTypeInfo { - signature, - func_ref: FuncRefIndex::reserved_value(), - }) - } - - /// Appends a new function to this module with the given type information. - pub fn push_escaped_function( - &mut self, - signature: SignatureIndex, - func_ref: FuncRefIndex, - ) -> FuncIndex { - self.functions.push(FunctionTypeInfo { - signature, - func_ref, - }) - } - - /// Returns the global initializer for the given index, or `Unsupported` error if the global is imported. - pub fn try_global_initializer( - &self, - index: GlobalIndex, - diagnostics: &DiagnosticsHandler, - ) -> WasmResult<&GlobalInit> { - if let Some(defined_index) = self.defined_global_index(index) { - Ok(&self.global_initializers[defined_index]) - } else { - unsupported_diag!(diagnostics, "Imported globals are not supported yet"); - } - } -} - -/// Type information about functions in a wasm module. -#[derive(Debug, Clone, Copy)] -pub struct FunctionTypeInfo { - /// The type of this function, indexed into the module-wide type tables for - /// a module compilation. - pub signature: SignatureIndex, - /// The index into the funcref table, if present. Note that this is - /// `reserved_value()` if the function does not escape from a module. - pub func_ref: FuncRefIndex, -} - -impl FunctionTypeInfo { - /// Returns whether this function's type is one that "escapes" the current - /// module, meaning that the function is exported, used in `ref.func`, used - /// in a table, etc. - pub fn is_escaping(&self) -> bool { - !self.func_ref.is_reserved_value() - } -} - -/// Index into the funcref table within a VMContext for a function. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] -pub struct FuncRefIndex(u32); -miden_hir::cranelift_entity::entity_impl!(FuncRefIndex); - -#[derive(Debug, Default)] -pub struct NameSection { - pub module_name: Option, - pub func_names: HashMap, - pub locals_names: HashMap>, - pub globals_names: HashMap, - pub data_segment_names: HashMap, -} diff --git a/frontend-wasm/src/module/module_env.rs b/frontend-wasm/src/module/module_env.rs deleted file mode 100644 index 556a037c5..000000000 --- a/frontend-wasm/src/module/module_env.rs +++ /dev/null @@ -1,806 +0,0 @@ -use crate::error::WasmResult; -use crate::module::types::{ - convert_func_type, convert_global_type, convert_table_type, convert_valtype, DataSegmentOffset, - DefinedFuncIndex, ElemIndex, EntityIndex, EntityType, FuncIndex, GlobalIndex, GlobalInit, - MemoryIndex, ModuleTypesBuilder, SignatureIndex, TableIndex, TypeIndex, WasmType, -}; -use crate::module::{FuncRefIndex, Initializer, Module, ModuleType, TableSegment}; -use crate::{unsupported_diag, WasmError, WasmTranslationConfig}; - -use miden_diagnostics::DiagnosticsHandler; -use miden_hir::cranelift_entity::packed_option::ReservedValue; -use miden_hir::cranelift_entity::PrimaryMap; -use std::collections::HashMap; -use std::convert::TryFrom; -use std::ops::Range; -use std::path::PathBuf; -use std::sync::Arc; -use wasmparser::types::{CoreTypeId, Types}; -use wasmparser::{ - CompositeType, CustomSectionReader, DataKind, ElementItems, ElementKind, Encoding, - ExternalKind, FuncToValidate, FunctionBody, NameSectionReader, Naming, Operator, Parser, - Payload, TypeRef, Validator, ValidatorResources, -}; - -use super::types::{DataSegment, DataSegmentIndex}; -use super::TableInitialValue; - -/// Object containing the standalone environment information. -pub struct ModuleEnvironment<'a, 'data> { - /// The current module being translated - result: ModuleTranslation<'data>, - - /// Intern'd types for this entire translation, shared by all modules. - types: &'a mut ModuleTypesBuilder, - - /// Wasmparser validator for the current module. - validator: &'a mut Validator, - - /// Configuration for the translation. - config: &'a WasmTranslationConfig, -} - -/// The result of translating via `ModuleEnvironment`. Function bodies are not -/// yet translated, and data initializers have not yet been copied out of the -/// original buffer. -#[derive(Default)] -pub struct ModuleTranslation<'data> { - /// Module information. - pub module: Module, - - /// References to the function bodies. - pub function_body_inputs: PrimaryMap>, - - /// A list of type signatures which are considered exported from this - /// module, or those that can possibly be called. - pub exported_signatures: Vec, - - /// DWARF debug information, if enabled, parsed from the module. - pub debuginfo: DebugInfoData<'data>, - - /// Set if debuginfo was found but it was not parsed due to `Tunables` - /// configuration. - pub has_unparsed_debuginfo: bool, - - /// List of data segments found in this module - pub data_segments: PrimaryMap>, - - /// When we're parsing the code section this will be incremented so we know - /// which function is currently being defined. - code_index: u32, - - /// The type information of the current module made available at the end of the - /// validation process. - types: Option, -} - -impl<'data> ModuleTranslation<'data> { - /// Returns a reference to the type information of the current module. - pub fn get_types(&self) -> &Types { - self.types - .as_ref() - .expect("module type information to be available") - } -} - -/// Contains function data: byte code and its offset in the module. -pub struct FunctionBodyData<'a> { - /// The body of the function, containing code and locals. - pub body: FunctionBody<'a>, - /// Validator for the function body - pub validator: FuncToValidate, -} - -#[derive(Debug, 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>, -} - -pub type Dwarf<'input> = gimli::Dwarf>; - -type Reader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>; - -#[derive(Debug, Default)] -pub struct WasmFileInfo { - pub path: Option, - pub code_section_offset: u64, - pub imported_func_count: u32, - pub funcs: Vec, -} - -#[derive(Debug)] -pub struct FunctionMetadata { - pub params: Box<[WasmType]>, - pub locals: Box<[(u32, WasmType)]>, -} - -impl<'a, 'data> ModuleEnvironment<'a, 'data> { - /// Allocates the environment data structures. - pub fn new( - config: &'a WasmTranslationConfig, - validator: &'a mut Validator, - types: &'a mut ModuleTypesBuilder, - ) -> Self { - Self { - result: ModuleTranslation::default(), - types, - config, - validator, - } - } - - /// Parse a wasm module using this environment. - /// - /// This function will parse the `data` provided with `parser`, - /// validating everything along the way with this environment's validator. - /// - /// The result of parsing, [`ModuleTranslation`], contains everything - /// necessary to translate functions afterwards - pub fn parse( - mut self, - parser: Parser, - data: &'data [u8], - diagnostics: &DiagnosticsHandler, - ) -> WasmResult> { - for payload in parser.parse_all(data) { - self.parse_payload(payload?, diagnostics)?; - } - Ok(self.result) - } - - /// Parses a single payload from the wasm module. - fn parse_payload( - &mut self, - payload: Payload<'data>, - diagnostics: &DiagnosticsHandler, - ) -> WasmResult<()> { - match payload { - Payload::Version { - num, - encoding, - range, - } => { - self.validator.version(num, encoding, &range)?; - match encoding { - Encoding::Module => {} - Encoding::Component => { - return Err(WasmError::Unsupported(format!("component model"))); - } - } - } - Payload::End(offset) => self.payload_end(offset)?, - 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::MemorySection(memories) => self.memory_section(memories)?, - Payload::TagSection(tags) => { - self.validator.tag_section(&tags)?; - // This feature isn't enabled at this time, so we should - // never get here. - unreachable!(); - } - Payload::GlobalSection(globals) => self.global_section(globals)?, - Payload::ExportSection(exports) => self.export_section(exports)?, - Payload::StartSection { func, range } => self.start_section(func, range)?, - Payload::ElementSection(elements) => self.element_section(elements)?, - 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)?; - // 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 - // Given that the total segment count might be much larger than - // 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())); - if let Err(e) = result { - log::warn!("failed to parse name section {:?}", e); - } - } - Payload::CustomSection(s) => self.dwarf_section(&s), - // It's expected that validation will probably reject other - // 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); - } - } - Ok(()) - } - - fn payload_end(&mut self, offset: usize) -> Result<(), WasmError> { - self.result.types = Some(self.validator.end(offset)?); - self.result.exported_signatures = self - .result - .module - .functions - .iter() - .filter_map(|(_, func)| { - if func.is_escaping() { - Some(func.signature) - } else { - None - } - }) - .collect(); - self.result.exported_signatures.sort_unstable(); - self.result.exported_signatures.dedup(); - Ok(()) - } - - fn type_section( - &mut self, - types: wasmparser::TypeSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.type_section(&types)?; - let num = usize::try_from(types.count()).unwrap(); - self.result.module.types.reserve(num); - self.types.reserve_wasm_signatures(num); - Ok(for i in 0..types.count() { - let types = self.validator.types(0).unwrap(); - let ty = types.core_type_at(i); - self.declare_type(ty.unwrap_sub())?; - }) - } - - fn import_section( - &mut self, - imports: wasmparser::ImportSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.import_section(&imports)?; - let cnt = usize::try_from(imports.count()).unwrap(); - self.result.module.initializers.reserve(cnt); - Ok(for entry in imports { - let import = entry?; - 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; - EntityType::Function(sig_index) - } - TypeRef::Memory(ty) => { - self.result.module.num_imported_memories += 1; - EntityType::Memory(ty.into()) - } - TypeRef::Global(ty) => { - self.result.module.num_imported_globals += 1; - EntityType::Global(convert_global_type(&ty)) - } - TypeRef::Table(ty) => { - self.result.module.num_imported_tables += 1; - EntityType::Table(convert_table_type(&ty)) - } - - // doesn't get past validation - TypeRef::Tag(_) => unreachable!(), - }; - self.declare_import(import.module, import.name, ty); - }) - } - - fn function_section( - &mut self, - functions: wasmparser::FunctionSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.function_section(&functions)?; - let cnt = usize::try_from(functions.count()).unwrap(); - self.result.module.functions.reserve_exact(cnt); - Ok(for entry in functions { - let sigindex = entry?; - let ty = TypeIndex::from_u32(sigindex); - let sig_index = self.result.module.types[ty].unwrap_function(); - self.result.module.push_function(sig_index); - }) - } - - fn table_section( - &mut self, - tables: wasmparser::TableSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.table_section(&tables)?; - let cnt = usize::try_from(tables.count()).unwrap(); - self.result.module.tables.reserve_exact(cnt); - Ok(for entry in tables { - let wasmparser::Table { ty, init } = entry?; - let table = convert_table_type(&ty); - self.result.module.tables.push(table); - let init = match init { - wasmparser::TableInit::RefNull => TableInitialValue::Null { - precomputed: Vec::new(), - }, - wasmparser::TableInit::Expr(cexpr) => { - let mut init_expr_reader = cexpr.get_binary_reader(); - match init_expr_reader.read_operator()? { - Operator::RefNull { hty: _ } => TableInitialValue::Null { - precomputed: Vec::new(), - }, - Operator::RefFunc { function_index } => { - let index = FuncIndex::from_u32(function_index); - self.flag_func_escaped(index); - TableInitialValue::FuncRef(index) - } - s => { - return Err(WasmError::Unsupported(format!( - "unsupported init expr in table section: {:?}", - s - ))); - } - } - } - }; - self.result - .module - .table_initialization - .initial_values - .push(init); - }) - } - - fn memory_section( - &mut self, - memories: wasmparser::MemorySectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.memory_section(&memories)?; - let cnt = usize::try_from(memories.count()).unwrap(); - assert_eq!(cnt, 1, "only one memory per module is supported"); - Ok(()) - } - - fn global_section( - &mut self, - globals: wasmparser::GlobalSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.global_section(&globals)?; - let cnt = usize::try_from(globals.count()).unwrap(); - self.result.module.globals.reserve_exact(cnt); - Ok(for entry in globals { - let wasmparser::Global { ty, init_expr } = entry?; - let mut init_expr_reader = init_expr.get_binary_reader(); - let initializer = match init_expr_reader.read_operator()? { - Operator::I32Const { value } => GlobalInit::I32Const(value), - Operator::I64Const { value } => GlobalInit::I64Const(value), - Operator::F32Const { value } => GlobalInit::F32Const(value.bits()), - Operator::F64Const { value } => GlobalInit::F64Const(value.bits()), - Operator::V128Const { value } => { - GlobalInit::V128Const(u128::from_le_bytes(*value.bytes())) - } - Operator::GlobalGet { global_index } => { - GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index)) - } - s => { - return Err(WasmError::Unsupported(format!( - "unsupported init expr in global section: {:?}", - s - ))); - } - }; - let ty = convert_global_type(&ty); - self.result.module.globals.push(ty); - self.result.module.global_initializers.push(initializer); - }) - } - - fn export_section( - &mut self, - exports: wasmparser::ExportSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.export_section(&exports)?; - let cnt = usize::try_from(exports.count()).unwrap(); - self.result.module.exports.reserve(cnt); - Ok(for entry in exports { - let wasmparser::Export { name, kind, index } = entry?; - let entity = match kind { - ExternalKind::Func => { - let index = FuncIndex::from_u32(index); - self.flag_func_escaped(index); - EntityIndex::Function(index) - } - ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(index)), - ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(index)), - ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(index)), - - // this never gets past validation - ExternalKind::Tag => unreachable!(), - }; - self.result - .module - .exports - .insert(String::from(name), entity); - }) - } - - fn start_section(&mut self, func: u32, range: Range) -> Result<(), WasmError> { - self.validator.start_section(func, &range)?; - let func_index = FuncIndex::from_u32(func); - self.flag_func_escaped(func_index); - debug_assert!(self.result.module.start_func.is_none()); - self.result.module.start_func = Some(func_index); - Ok(()) - } - - fn element_section( - &mut self, - elements: wasmparser::ElementSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.element_section(&elements)?; - Ok(for (index, entry) in elements.into_iter().enumerate() { - let wasmparser::Element { - kind, - items, - range: _, - } = entry?; - - // Build up a list of `FuncIndex` corresponding to all the - // entries listed in this segment. Note that it's not - // possible to create anything other than a `ref.null - // extern` for externref segments, so those just get - // translated to the reserved value of `FuncIndex`. - let mut elements = Vec::new(); - match items { - ElementItems::Functions(funcs) => { - elements.reserve(usize::try_from(funcs.count()).unwrap()); - for func in funcs { - let func = FuncIndex::from_u32(func?); - self.flag_func_escaped(func); - elements.push(func); - } - } - 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()? { - Operator::RefNull { .. } => FuncIndex::reserved_value(), - Operator::RefFunc { function_index } => { - let func = FuncIndex::from_u32(function_index); - self.flag_func_escaped(func); - func - } - s => { - return Err(WasmError::Unsupported(format!( - "unsupported init expr in element section: {:?}", - s - ))); - } - }; - elements.push(func); - } - } - } - - match kind { - ElementKind::Active { - table_index, - offset_expr, - } => { - 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 - ))); - } - }; - - self.result - .module - .table_initialization - .segments - .push(TableSegment { - table_index, - base, - offset, - elements: elements.into(), - }); - } - - ElementKind::Passive => { - let elem_index = ElemIndex::from_u32(index as u32); - let index = self.result.module.passive_elements.len(); - self.result.module.passive_elements.push(elements.into()); - self.result - .module - .passive_elements_map - .insert(elem_index, index); - } - - ElementKind::Declared => {} - } - }) - } - - fn code_section_start(&mut self, count: u32, range: Range) -> Result<(), WasmError> { - self.validator.code_section_start(count, &range)?; - 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; - Ok(()) - } - - fn code_section_entry(&mut self, mut body: FunctionBody<'data>) -> Result<(), WasmError> { - let validator = self.validator.code_section_entry(&body)?; - 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?; - let ty = convert_valtype(ty); - locals.push((cnt, ty)); - } - self.result - .debuginfo - .wasm_file - .funcs - .push(FunctionMetadata { - locals: locals.into_boxed_slice(), - params: sig.params().into(), - }); - } - body.allow_memarg64(self.validator.features().memory64); - self.result - .function_body_inputs - .push(FunctionBodyData { validator, body }); - self.result.code_index += 1; - Ok(()) - } - - fn data_section( - &mut self, - data_section: wasmparser::DataSectionReader<'data>, - diagnostics: &DiagnosticsHandler, - ) -> WasmResult<()> { - self.validator.data_section(&data_section)?; - let cnt = usize::try_from(data_section.count()).unwrap(); - self.result.data_segments.reserve_exact(cnt); - for entry in data_section.into_iter() { - let wasmparser::Data { - kind, - data, - range: _, - } = entry?; - match kind { - DataKind::Active { - memory_index, - offset_expr, - } => { - assert_eq!( - memory_index, 0, - "data section memory index must be 0 (only one memory per module is supported)" - ); - let mut offset_expr_reader = offset_expr.get_binary_reader(); - let offset = match offset_expr_reader.read_operator()? { - Operator::I32Const { value } => DataSegmentOffset::I32Const(value), - Operator::GlobalGet { global_index } => { - DataSegmentOffset::GetGlobal(GlobalIndex::from_u32(global_index)) - } - ref s => { - unsupported_diag!( - diagnostics, - "unsupported init expr in data section offset: {:?}", - s - ); - } - }; - let segment = DataSegment { offset, data }; - self.result.data_segments.push(segment); - } - DataKind::Passive => { - return Err(WasmError::Unsupported( - "unsupported passive data segment in data section".to_string(), - )); - } - } - } - Ok(()) - } - - /// Parses the Name section of the wasm module. - fn name_section(&mut self, names: NameSectionReader<'data>) -> WasmResult<()> { - for subsection in names { - match subsection? { - wasmparser::Name::Function(names) => { - for name in names { - let Naming { index, name } = name?; - // Skip this naming if it's naming a function that - // doesn't actually exist. - if (index as usize) >= self.result.module.functions.len() { - continue; - } - - // Store the name unconditionally, regardless of - // whether we're parsing debuginfo, since function - // names are almost always present in the - // final compilation artifact. - let index = FuncIndex::from_u32(index); - self.result - .module - .name_section - .func_names - .insert(index, name.to_string()); - } - } - wasmparser::Name::Module { name, .. } => { - self.result.module.name_section.module_name = Some(name.to_string()); - } - wasmparser::Name::Local(reader) => { - if !self.config.generate_native_debuginfo { - continue; - } - for f in reader { - let f = f?; - // 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?; - - self.result - .module - .name_section - .locals_names - .entry(FuncIndex::from_u32(f.index)) - .or_insert(HashMap::new()) - .insert(index, name.to_string()); - } - } - } - wasmparser::Name::Global(names) => { - for name in names { - let Naming { index, name } = name?; - if index != u32::max_value() { - self.result - .module - .name_section - .globals_names - .insert(GlobalIndex::from_u32(index), name.to_string()); - } - } - } - wasmparser::Name::Data(names) => { - for name in names { - let Naming { index, name } = name?; - if index != u32::max_value() { - self.result - .module - .name_section - .data_segment_names - .insert(DataSegmentIndex::from_u32(index), name.to_string()); - } - } - } - wasmparser::Name::Label(_) - | wasmparser::Name::Type(_) - | wasmparser::Name::Table(_) - | wasmparser::Name::Memory(_) - | wasmparser::Name::Element(_) - | wasmparser::Name::Unknown { .. } => {} - } - } - Ok(()) - } - - fn dwarf_section(&mut self, section: &CustomSectionReader<'data>) { - let name = section.name(); - if !name.starts_with(".debug_") { - return; - } - if !self.config.generate_native_debuginfo && !self.config.parse_wasm_debuginfo { - self.result.has_unparsed_debuginfo = true; - return; - } - let info = &mut self.result.debuginfo; - let dwarf = &mut info.dwarf; - let endian = gimli::LittleEndian; - let data = section.data(); - let slice = gimli::EndianSlice::new(data, endian); - - match name { - // `gimli::Dwarf` fields. - ".debug_abbrev" => dwarf.debug_abbrev = gimli::DebugAbbrev::new(data, endian), - ".debug_addr" => dwarf.debug_addr = gimli::DebugAddr::from(slice), - ".debug_info" => dwarf.debug_info = gimli::DebugInfo::new(data, endian), - ".debug_line" => dwarf.debug_line = gimli::DebugLine::new(data, endian), - ".debug_line_str" => dwarf.debug_line_str = gimli::DebugLineStr::from(slice), - ".debug_str" => dwarf.debug_str = gimli::DebugStr::new(data, endian), - ".debug_str_offsets" => dwarf.debug_str_offsets = gimli::DebugStrOffsets::from(slice), - ".debug_str_sup" => { - let mut dwarf_sup: Dwarf<'data> = Default::default(); - dwarf_sup.debug_str = gimli::DebugStr::from(slice); - dwarf.sup = Some(Arc::new(dwarf_sup)); - } - ".debug_types" => dwarf.debug_types = gimli::DebugTypes::from(slice), - - // Additional fields. - ".debug_loc" => info.debug_loc = gimli::DebugLoc::from(slice), - ".debug_loclists" => info.debug_loclists = gimli::DebugLocLists::from(slice), - ".debug_ranges" => info.debug_ranges = gimli::DebugRanges::new(data, endian), - ".debug_rnglists" => info.debug_rnglists = gimli::DebugRngLists::new(data, endian), - - // We don't use these at the moment. - ".debug_aranges" | ".debug_pubnames" | ".debug_pubtypes" => return, - - other => { - log::warn!("unknown debug section `{}`", other); - return; - } - } - - dwarf.ranges = gimli::RangeLists::new(info.debug_ranges, info.debug_rnglists); - dwarf.locations = gimli::LocationLists::new(info.debug_loc, info.debug_loclists); - } - - /// Declares a new import with the `module` and `field` names, importing the - /// `ty` specified. - fn declare_import(&mut self, module: &'data str, field: &'data str, ty: EntityType) { - let index = self.push_type(ty); - self.result.module.initializers.push(Initializer::Import { - name: module.to_owned(), - field: field.to_owned(), - index, - }); - } - - fn push_type(&mut self, ty: EntityType) -> EntityIndex { - match ty { - EntityType::Function(ty) => EntityIndex::Function(self.result.module.push_function(ty)), - EntityType::Table(ty) => EntityIndex::Table(self.result.module.tables.push(ty)), - EntityType::Memory(ty) => EntityIndex::Memory(self.result.module.memories.push(ty)), - EntityType::Global(ty) => EntityIndex::Global(self.result.module.globals.push(ty)), - EntityType::Tag(_) => unimplemented!(), - } - } - - fn flag_func_escaped(&mut self, func: FuncIndex) { - let ty = &mut self.result.module.functions[func]; - // If this was already assigned a funcref index no need to re-assign it. - if ty.is_escaping() { - return; - } - let index = self.result.module.num_escaped_funcs as u32; - ty.func_ref = FuncRefIndex::from_u32(index); - self.result.module.num_escaped_funcs += 1; - } - - fn declare_type(&mut self, id: CoreTypeId) -> WasmResult<()> { - 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) => { - 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!(), - } - Ok(()) - } -} diff --git a/frontend-wasm/src/module/translate.rs b/frontend-wasm/src/module/translate.rs deleted file mode 100644 index 779f105b6..000000000 --- a/frontend-wasm/src/module/translate.rs +++ /dev/null @@ -1,136 +0,0 @@ -use miden_diagnostics::{DiagnosticsHandler, SourceSpan}; -use miden_hir::{CallConv, ConstantData, Linkage, ModuleBuilder}; -use wasmparser::{Validator, WasmFeatures}; - -use crate::{ - error::WasmResult, - module::func_translator::FuncTranslator, - module::module_env::{FunctionBodyData, ModuleEnvironment, ModuleTranslation}, - module::types::{ir_func_sig, ir_func_type, ir_type, ModuleTypes}, - WasmError, WasmTranslationConfig, -}; - -use super::Module; - -/// Translate a valid Wasm core module binary into Miden IR module -pub fn translate_module( - wasm: &[u8], - config: &WasmTranslationConfig, - diagnostics: &DiagnosticsHandler, -) -> WasmResult { - let wasm_features = WasmFeatures::default(); - let mut validator = Validator::new_with_features(wasm_features); - let parser = wasmparser::Parser::new(0); - let mut module_types_builder = Default::default(); - let translation = ModuleEnvironment::new(config, &mut validator, &mut module_types_builder) - .parse(parser, wasm, diagnostics)?; - let module_types = module_types_builder.finish(); - build_ir_module(translation, module_types, config, diagnostics) -} - -fn build_ir_module( - mut translation: ModuleTranslation, - module_types: ModuleTypes, - config: &WasmTranslationConfig, - diagnostics: &DiagnosticsHandler, -) -> WasmResult { - if translation.module.name_section.module_name.is_none() { - translation.module.name_section.module_name = Some(config.module_name_fallback.clone()); - } - let wasm_module = &translation.module; - let name = wasm_module - .name_section - .module_name - .clone() - .expect("Module name should be set by this point"); - let mut module_builder = ModuleBuilder::new(name.as_str()); - build_globals(&wasm_module, &mut module_builder, diagnostics)?; - build_data_segments(&translation, &mut module_builder, diagnostics)?; - let mut func_translator = FuncTranslator::new(); - for (defined_func_idx, body_data) in translation.function_body_inputs { - let func_index = wasm_module.func_index(defined_func_idx); - let func_type = wasm_module.functions[func_index]; - let func_name = wasm_module - .name_section - .func_names - .get(&func_index) - .cloned() - .unwrap_or(format!("func{}", func_index.as_u32())); - let wasm_func_type = module_types[func_type.signature].clone(); - let ir_func_type = ir_func_type(&wasm_func_type)?; - 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; - let mut func_validator = validator.into_validator(Default::default()); - func_translator.translate_body( - &body, - &mut module_func_builder, - &wasm_module, - &module_types, - diagnostics, - &mut func_validator, - )?; - module_func_builder - .build(diagnostics) - .map_err(|_| WasmError::InvalidFunctionError)?; - } - let module = module_builder.build(); - Ok(*module) -} - -fn build_globals( - wasm_module: &Module, - module_builder: &mut ModuleBuilder, - diagnostics: &DiagnosticsHandler, -) -> Result<(), WasmError> { - Ok(for (global_idx, global) in &wasm_module.globals { - let global_name = wasm_module - .name_section - .globals_names - .get(&global_idx) - .cloned() - .unwrap_or(format!("gv{}", global_idx.as_u32())); - let global_init = wasm_module.try_global_initializer(global_idx, diagnostics)?; - let init = ConstantData::from(global_init.to_le_bytes(&wasm_module, diagnostics)?); - if let Err(e) = module_builder.declare_global_variable( - &global_name, - ir_type(global.ty.clone())?, - Linkage::External, - Some(init.clone()), - SourceSpan::default(), - ) { - let message = format!("Failed to declare global variable '{global_name}' with initializer '{init}' with error: {:?}", e); - diagnostics - .diagnostic(miden_diagnostics::Severity::Error) - .with_message(message.clone()) - .emit(); - return Err(WasmError::Unexpected(message)); - } - }) -} - -fn build_data_segments( - translation: &ModuleTranslation, - module_builder: &mut ModuleBuilder, - diagnostics: &DiagnosticsHandler, -) -> Result<(), WasmError> { - for (data_segment_idx, data_segment) in &translation.data_segments { - let data_segment_name = - translation.module.name_section.data_segment_names[&data_segment_idx].clone(); - let readonly = data_segment_name.contains(".rodata"); - let init = ConstantData::from(data_segment.data); - let offset = data_segment - .offset - .as_i32(&translation.module, diagnostics)? as u32; - let size = init.len() as u32; - if let Err(e) = module_builder.declare_data_segment(offset, size, init, readonly) { - let message = format!("Failed to declare data segment '{data_segment_name}' with size '{size}' at '{offset}' with error: {:?}", e); - diagnostics - .diagnostic(miden_diagnostics::Severity::Error) - .with_message(message.clone()) - .emit(); - return Err(WasmError::Unexpected(message)); - } - } - Ok(()) -} diff --git a/frontend-wasm/src/module/types.rs b/frontend-wasm/src/module/types.rs deleted file mode 100644 index e3bde0133..000000000 --- a/frontend-wasm/src/module/types.rs +++ /dev/null @@ -1,696 +0,0 @@ -//! Types for parsed core WebAssembly modules. - -use core::fmt; -use miden_hir::{AbiParam, CallConv, Linkage, Signature}; -use std::collections::HashMap; -use std::ops::Index; -use wasmparser::types::CoreTypeId; - -use miden_diagnostics::DiagnosticsHandler; -use miden_hir::cranelift_entity::PrimaryMap; -use miden_hir_type as hir; - -use crate::error::WasmResult; -use crate::module::Module; -use crate::{unsupported_diag, WasmError}; - -/// WebAssembly page sizes are defined to be 64KiB. -pub const WASM_PAGE_SIZE: u32 = 0x10000; - -/// The number of pages (for 32-bit modules) we can have before we run out of -/// byte index space. -pub const WASM32_MAX_PAGES: u64 = 1 << 16; - -/// The number of pages (for 64-bit modules) we can have before we run out of -/// byte index space. -pub const WASM64_MAX_PAGES: u64 = 1 << 48; - -macro_rules! indices { - ($( - $(#[$a:meta])* - pub struct $name:ident(u32); - )*) => ($( - $(#[$a])* - #[derive( - Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug - )] - #[repr(transparent)] - pub struct $name(u32); - miden_hir::cranelift_entity::entity_impl!($name); - )*); -} - -indices! { -/// Index type of a function (imported or defined) inside the WebAssembly module. -pub struct FuncIndex(u32); - -/// Index type of a defined function inside the WebAssembly module. -pub struct DefinedFuncIndex(u32); - -/// Index type of a defined table inside the WebAssembly module. -pub struct DefinedTableIndex(u32); - -/// Index type of a defined memory inside the WebAssembly module. -pub struct DefinedMemoryIndex(u32); - -/// Index type of a defined memory inside the WebAssembly module. -pub struct OwnedMemoryIndex(u32); - -/// Index type of a defined global inside the WebAssembly module. -pub struct DefinedGlobalIndex(u32); - -/// Index type of a table (imported or defined) inside the WebAssembly module. -pub struct TableIndex(u32); - -/// Index type of a global variable (imported or defined) inside the WebAssembly module. -pub struct GlobalIndex(u32); - -/// Index type of a linear memory (imported or defined) inside the WebAssembly module. -pub struct MemoryIndex(u32); - -/// Index type of a passive data segment inside the WebAssembly module. -pub struct DataIndex(u32); - -/// Index type of a passive element segment inside the WebAssembly module. -pub struct ElemIndex(u32); - -/// Index type of a type inside the WebAssembly module. -pub struct TypeIndex(u32); - -/// Index type of a data segment inside the WebAssembly module. -pub struct DataSegmentIndex(u32); - -/// Index type of a signature (imported or defined) inside the WebAssembly module. -pub struct SignatureIndex(u32); -} - -/// WebAssembly value type -- equivalent of `wasmparser`'s Type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum WasmType { - /// I32 type - I32, - /// I64 type - I64, - /// F32 type - F32, - /// F64 type - F64, - /// V128 type - V128, - /// Reference type - Ref(WasmRefType), -} - -impl fmt::Display for WasmType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - WasmType::I32 => write!(f, "i32"), - WasmType::I64 => write!(f, "i64"), - WasmType::F32 => write!(f, "f32"), - WasmType::F64 => write!(f, "f64"), - WasmType::V128 => write!(f, "v128"), - WasmType::Ref(rt) => write!(f, "{rt}"), - } - } -} - -/// WebAssembly reference type -- equivalent of `wasmparser`'s RefType -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct WasmRefType { - pub nullable: bool, - pub heap_type: WasmHeapType, -} - -impl WasmRefType { - pub const EXTERNREF: WasmRefType = WasmRefType { - nullable: true, - heap_type: WasmHeapType::Extern, - }; - pub const FUNCREF: WasmRefType = WasmRefType { - nullable: true, - heap_type: WasmHeapType::Func, - }; -} - -impl fmt::Display for WasmRefType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Self::FUNCREF => write!(f, "funcref"), - Self::EXTERNREF => write!(f, "externref"), - _ => { - if self.nullable { - write!(f, "(ref null {})", self.heap_type) - } else { - write!(f, "(ref {})", self.heap_type) - } - } - } - } -} - -/// WebAssembly heap type -- equivalent of `wasmparser`'s HeapType -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum WasmHeapType { - /// The abstract, untyped (any) function. - /// - /// Introduced in the references-types proposal. - Func, - /// The abstract, external heap type. - /// - /// Introduced in the references-types proposal. - Extern, - /// Typed function. - TypedFunc(SignatureIndex), -} - -impl fmt::Display for WasmHeapType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Func => write!(f, "func"), - Self::Extern => write!(f, "extern"), - Self::TypedFunc(i) => write!(f, "func_sig{}", i.as_u32()), - } - } -} - -/// WebAssembly function type -- equivalent of `wasmparser`'s FuncType. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct WasmFuncType { - params: Box<[WasmType]>, - externref_params_count: usize, - returns: Box<[WasmType]>, - externref_returns_count: usize, -} - -impl WasmFuncType { - #[inline] - pub fn new(params: Box<[WasmType]>, returns: Box<[WasmType]>) -> Self { - let externref_params_count = params - .iter() - .filter(|p| match **p { - WasmType::Ref(rt) => rt.heap_type == WasmHeapType::Extern, - _ => false, - }) - .count(); - let externref_returns_count = returns - .iter() - .filter(|r| match **r { - WasmType::Ref(rt) => rt.heap_type == WasmHeapType::Extern, - _ => false, - }) - .count(); - WasmFuncType { - params, - externref_params_count, - returns, - externref_returns_count, - } - } - - /// Function params types. - #[inline] - pub fn params(&self) -> &[WasmType] { - &self.params - } - - /// How many `externref`s are in this function's params? - #[inline] - pub fn externref_params_count(&self) -> usize { - self.externref_params_count - } - - /// Returns params types. - #[inline] - pub fn returns(&self) -> &[WasmType] { - &self.returns - } - - /// How many `externref`s are in this function's returns? - #[inline] - pub fn externref_returns_count(&self) -> usize { - self.externref_returns_count - } -} - -/// An index of an entity. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] -pub enum EntityIndex { - /// Function index. - Function(FuncIndex), - /// Table index. - Table(TableIndex), - /// Memory index. - Memory(MemoryIndex), - /// Global index. - Global(GlobalIndex), -} - -/// A type of an item in a wasm module where an item is typically something that -/// can be exported. -#[allow(missing_docs)] -#[derive(Clone, Debug)] -pub enum EntityType { - /// A global variable with the specified content type - Global(Global), - /// A linear memory with the specified limits - Memory(Memory), - /// An event definition. - Tag(Tag), - /// A table with the specified element type and limits - Table(Table), - /// A function type where the index points to the type section and records a - /// function signature. - Function(SignatureIndex), -} - -impl EntityType { - /// Assert that this entity is a global - pub fn unwrap_global(&self) -> &Global { - match self { - EntityType::Global(g) => g, - _ => panic!("not a global"), - } - } - - /// Assert that this entity is a memory - pub fn unwrap_memory(&self) -> &Memory { - match self { - EntityType::Memory(g) => g, - _ => panic!("not a memory"), - } - } - - /// Assert that this entity is a tag - pub fn unwrap_tag(&self) -> &Tag { - match self { - EntityType::Tag(g) => g, - _ => panic!("not a tag"), - } - } - - /// Assert that this entity is a table - pub fn unwrap_table(&self) -> &Table { - match self { - EntityType::Table(g) => g, - _ => panic!("not a table"), - } - } - - /// Assert that this entity is a function - pub fn unwrap_func(&self) -> SignatureIndex { - match self { - EntityType::Function(g) => *g, - _ => panic!("not a func"), - } - } -} - -/// A WebAssembly global. -#[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub struct Global { - /// The Wasm type of the value stored in the global. - pub ty: WasmType, - /// A flag indicating whether the value may change at runtime. - pub mutability: bool, -} - -/// Globals are initialized via the `const` operators or by referring to another import. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -pub enum GlobalInit { - /// An `i32.const`. - I32Const(i32), - /// An `i64.const`. - I64Const(i64), - /// An `f32.const`. - F32Const(u32), - /// An `f64.const`. - F64Const(u64), - /// A `vconst`. - V128Const(u128), - /// A `global.get` of another global. - GetGlobal(GlobalIndex), -} - -impl GlobalInit { - /// Serialize the initializer constant expression into bytes (little-endian order). - pub fn to_le_bytes( - self, - module: &Module, - diagnostics: &DiagnosticsHandler, - ) -> WasmResult> { - Ok(match self { - GlobalInit::I32Const(x) => x.to_le_bytes().to_vec(), - GlobalInit::I64Const(x) => x.to_le_bytes().to_vec(), - GlobalInit::F32Const(x) => x.to_le_bytes().to_vec(), - GlobalInit::F64Const(x) => x.to_le_bytes().to_vec(), - GlobalInit::V128Const(x) => x.to_le_bytes().to_vec(), - GlobalInit::GetGlobal(global_idx) => { - let global_init = module.try_global_initializer(global_idx, diagnostics)?; - global_init.to_le_bytes(module, diagnostics)? - } - }) - } - - pub fn as_i32(&self, module: &Module, diagnostics: &DiagnosticsHandler) -> WasmResult { - Ok(match self { - GlobalInit::I32Const(x) => *x, - GlobalInit::GetGlobal(global_idx) => { - let global_init = module.try_global_initializer(*global_idx, diagnostics)?; - global_init.as_i32(module, diagnostics)? - } - g => { - unsupported_diag!(diagnostics, "Expected global init to be i32, got: {:?}", g); - } - }) - } -} - -/// WebAssembly table. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -pub struct Table { - /// The table elements' Wasm type. - pub wasm_ty: WasmRefType, - /// The minimum number of elements in the table. - pub minimum: u32, - /// The maximum number of elements in the table. - pub maximum: Option, -} - -/// WebAssembly linear memory. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -pub struct Memory { - /// The minimum number of pages in the memory. - pub minimum: u64, - /// The maximum number of pages in the memory. - pub maximum: Option, - /// Whether or not this is a 64-bit memory - pub memory64: bool, -} - -impl From for Memory { - fn from(ty: wasmparser::MemoryType) -> Memory { - Memory { - minimum: ty.initial, - maximum: ty.maximum, - memory64: ty.memory64, - } - } -} - -/// WebAssembly event. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -pub struct Tag { - /// The event signature type. - pub ty: TypeIndex, -} - -impl From for Tag { - fn from(ty: wasmparser::TagType) -> Tag { - match ty.kind { - wasmparser::TagKind::Exception => Tag { - ty: TypeIndex::from_u32(ty.func_type_idx), - }, - } - } -} -/// Offset of a data segment inside a linear memory. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -pub enum DataSegmentOffset { - /// An `i32.const` offset. - I32Const(i32), - /// An offset as a `global.get` of another global. - GetGlobal(GlobalIndex), -} - -impl DataSegmentOffset { - /// Returns the offset as a i32, resolving the global if necessary. - pub fn as_i32(&self, module: &Module, diagnostics: &DiagnosticsHandler) -> WasmResult { - Ok(match self { - DataSegmentOffset::I32Const(x) => *x, - 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) - .with_message(format!( - "Failed to get data segment offset from global init {:?} with global index {global_idx:?}", - global_init, - )) - .emit(); - return Err(e); - } - Ok(v) => v, - } - } - }) - } -} - -/// A WebAssembly data segment. -/// https://www.w3.org/TR/wasm-core-1/#data-segments%E2%91%A0 -pub struct DataSegment<'a> { - /// The offset of the data segment inside the linear memory. - pub offset: DataSegmentOffset, - /// The initialization data. - pub data: &'a [u8], -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub struct BlockType { - pub params: Vec, - pub results: Vec, -} - -impl BlockType { - pub fn from_wasm( - block_ty: &wasmparser::BlockType, - mod_types: &ModuleTypes, - ) -> WasmResult { - Ok(match block_ty { - wasmparser::BlockType::Empty => Self::default(), - wasmparser::BlockType::Type(ty) => Self { - params: vec![], - results: vec![ir_type(convert_valtype(*ty))?], - }, - 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)) - .collect::>>()?; - let results = func_type - .returns() - .iter() - .map(|t| ir_type(*t)) - .collect::>>()?; - Self { params, results } - } - }) - } -} - -/// Note that accesing this type is primarily done through the `Index` -/// implementations for this type. -#[derive(Default)] -pub struct ModuleTypes { - wasm_signatures: PrimaryMap, -} - -impl ModuleTypes { - /// Returns an iterator over all the wasm function signatures found within - /// this module. - pub fn wasm_signatures(&self) -> impl Iterator { - self.wasm_signatures.iter() - } -} - -impl Index for ModuleTypes { - type Output = WasmFuncType; - - fn index(&self, sig: SignatureIndex) -> &WasmFuncType { - &self.wasm_signatures[sig] - } -} - -/// A builder for [`ModuleTypes`]. -#[derive(Default)] -pub struct ModuleTypesBuilder { - types: ModuleTypes, - interned_func_types: HashMap, - wasmparser_to_wasmtime: HashMap, -} - -impl ModuleTypesBuilder { - /// Reserves space for `amt` more type signatures. - pub fn reserve_wasm_signatures(&mut self, amt: usize) { - self.types.wasm_signatures.reserve(amt); - } - - /// Interns the `sig` specified and returns a unique `SignatureIndex` that - /// can be looked up within [`ModuleTypes`] to recover the [`WasmFuncType`] - /// at runtime. - pub fn wasm_func_type(&mut self, id: CoreTypeId, sig: WasmFuncType) -> SignatureIndex { - let sig = self.intern_func_type(sig); - self.wasmparser_to_wasmtime.insert(id, sig); - sig - } - - fn intern_func_type(&mut self, sig: WasmFuncType) -> SignatureIndex { - if let Some(idx) = self.interned_func_types.get(&sig) { - return *idx; - } - - let idx = self.types.wasm_signatures.push(sig.clone()); - self.interned_func_types.insert(sig, idx); - return idx; - } - - /// Returns the result [`ModuleTypes`] of this builder. - pub fn finish(self) -> ModuleTypes { - self.types - } - - /// Returns an iterator over all the wasm function signatures found within - /// this module. - pub fn wasm_signatures(&self) -> impl Iterator { - self.types.wasm_signatures() - } -} - -// Forward the indexing impl to the internal `ModuleTypes` -impl Index for ModuleTypesBuilder -where - ModuleTypes: Index, -{ - type Output = >::Output; - - fn index(&self, sig: T) -> &Self::Output { - &self.types[sig] - } -} - -/// Converts a Wasm function type into a Miden IR function type -pub fn ir_func_type(ty: &WasmFuncType) -> WasmResult { - let params = ty - .params() - .iter() - .map(|t| ir_type(*t)) - .collect::>>()?; - let results = ty - .returns() - .iter() - .map(|t| ir_type(*t)) - .collect::>>()?; - Ok(hir::FunctionType { results, params }) -} - -/// Converts a Wasm type into a Miden IR type -pub fn ir_type(ty: WasmType) -> WasmResult { - Ok(match ty { - WasmType::I32 => hir::Type::I32, - WasmType::I64 => hir::Type::I64, - WasmType::F32 => { - return Err(WasmError::Unsupported( - "no f32 type in Miden IR".to_string(), - )) - } - WasmType::F64 => hir::Type::F64, - 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(), - )); - } - }) -} - -/// Makes an IR function signature from a Wasm function type -pub fn ir_func_sig( - func_type: &hir::FunctionType, - call_conv: CallConv, - linkage: Linkage, -) -> Signature { - Signature { - params: func_type - .params - .iter() - .map(|ty| AbiParam::new(ty.clone())) - .collect(), - results: func_type - .results - .iter() - .map(|ty| AbiParam::new(ty.clone())) - .collect(), - cc: call_conv, - linkage, - } -} - -/// Converts a wasmparser table type into a wasmtime type -pub fn convert_global_type(ty: &wasmparser::GlobalType) -> Global { - Global { - ty: convert_valtype(ty.content_type), - mutability: ty.mutable, - } -} - -/// Converts a wasmparser table type into a wasmtime type -pub fn convert_table_type(ty: &wasmparser::TableType) -> Table { - Table { - wasm_ty: convert_ref_type(ty.element_type), - minimum: ty.initial, - maximum: ty.maximum, - } -} - -/// Converts a wasmparser function type to a wasmtime type -pub fn convert_func_type(ty: &wasmparser::FuncType) -> WasmFuncType { - let params = ty.params().iter().map(|t| convert_valtype(*t)).collect(); - let results = ty.results().iter().map(|t| convert_valtype(*t)).collect(); - WasmFuncType::new(params, results) -} - -/// Converts a wasmparser value type to a wasmtime type -pub fn convert_valtype(ty: wasmparser::ValType) -> WasmType { - match ty { - wasmparser::ValType::I32 => WasmType::I32, - wasmparser::ValType::I64 => WasmType::I64, - wasmparser::ValType::F32 => WasmType::F32, - wasmparser::ValType::F64 => WasmType::F64, - wasmparser::ValType::V128 => WasmType::V128, - wasmparser::ValType::Ref(t) => WasmType::Ref(convert_ref_type(t)), - } -} - -/// Converts a wasmparser reference type to a wasmtime type -pub fn convert_ref_type(ty: wasmparser::RefType) -> WasmRefType { - WasmRefType { - nullable: ty.is_nullable(), - heap_type: convert_heap_type(ty.heap_type()), - } -} - -/// Converts a wasmparser heap type to a wasmtime type -pub fn convert_heap_type(ty: wasmparser::HeapType) -> WasmHeapType { - 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:?}"); - } - } -} diff --git a/frontend-wasm/src/ssa.rs b/frontend-wasm/src/ssa.rs deleted file mode 100644 index bce273c82..000000000 --- a/frontend-wasm/src/ssa.rs +++ /dev/null @@ -1,511 +0,0 @@ -//! A SSA-building API that handles incomplete CFGs. -//! -//! The algorithm is based upon Braun M., Buchwald S., Hack S., Leißa R., Mallon C., -//! Zwinkau A. (2013) Simple and Efficient Construction of Static Single Assignment Form. -//! In: Jhala R., De Bosschere K. (eds) Compiler Construction. CC 2013. -//! Lecture Notes in Computer Science, vol 7791. Springer, Berlin, Heidelberg -//! -//! -//! -//! Based on Cranelift's Wasm -> CLIF translator v11.0.0 - -use core::mem; -use miden_diagnostics::SourceSpan; -use miden_hir::cranelift_entity::packed_option::PackedOption; -use miden_hir::cranelift_entity::{entity_impl, EntityList, EntitySet, ListPool, SecondaryMap}; -use miden_hir::{Block, DataFlowGraph, Inst, Value}; -use miden_hir_type::Type; - -/// Structure containing the data relevant the construction of SSA for a given function. -/// -/// The parameter struct `Variable` corresponds to the way variables are represented in the -/// non-SSA language you're translating from. -/// -/// The SSA building relies on information about the variables used and defined. -/// -/// This SSA building module allows you to def and use variables on the fly while you are -/// constructing the CFG, no need for a separate SSA pass after the CFG is completed. -/// -/// A basic block is said _filled_ if all the instruction that it contains have been translated, -/// and it is said _sealed_ if all of its predecessors have been declared. Only filled predecessors -/// can be declared. -#[derive(Default)] -pub struct SSABuilder { - /// Records for every variable and for every relevant block, the last definition of - /// the variable in the block. - variables: SecondaryMap>>, - - /// Records the position of the basic blocks and the list of values used but not defined in the - /// block. - ssa_blocks: SecondaryMap, - - /// Call stack for use in the `use_var`/`predecessors_lookup` state machine. - calls: Vec, - /// Result stack for use in the `use_var`/`predecessors_lookup` state machine. - results: Vec, - - /// Side effects accumulated in the `use_var`/`predecessors_lookup` state machine. - side_effects: SideEffects, - - /// Reused storage for cycle-detection. - visited: EntitySet, - - /// Storage for pending variable definitions. - variable_pool: ListPool, - - /// Storage for predecessor definitions. - inst_pool: ListPool, -} - -/// An opaque reference to a mutable variable. -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct Variable(u32); -entity_impl!(Variable, "var"); - -/// Side effects of a `use_var` or a `seal_block` method call. -#[derive(Default)] -pub struct SideEffects { - /// When a variable is used but has never been defined before (this happens in the case of - /// unreachable code), a placeholder `iconst` or `fconst` value is added to the right `Block`. - /// This field signals if it is the case and return the `Block` to which the initialization has - /// been added. - pub instructions_added_to_blocks: Vec, -} - -impl SideEffects { - fn is_empty(&self) -> bool { - self.instructions_added_to_blocks.is_empty() - } -} - -#[derive(Clone)] -enum Sealed { - No { - // List of current Block arguments for which an earlier def has not been found yet. - undef_variables: EntityList, - }, - Yes, -} - -impl Default for Sealed { - fn default() -> Self { - Sealed::No { - undef_variables: EntityList::new(), - } - } -} - -#[derive(Clone, Default)] -struct SSABlockData { - // The predecessors of the Block with the block and branch instruction. - predecessors: EntityList, - // A block is sealed if all of its predecessors have been declared. - sealed: Sealed, - // If this block is sealed and it has exactly one predecessor, this is that predecessor. - single_predecessor: PackedOption, -} - -impl SSABuilder { - /// Clears a `SSABuilder` from all its data, letting it in a pristine state without - /// deallocating memory. - pub fn clear(&mut self) { - self.variables.clear(); - self.ssa_blocks.clear(); - self.variable_pool.clear(); - self.inst_pool.clear(); - debug_assert!(self.calls.is_empty()); - debug_assert!(self.results.is_empty()); - debug_assert!(self.side_effects.is_empty()); - } - - /// Tests whether an `SSABuilder` is in a cleared state. - pub fn is_empty(&self) -> bool { - self.variables.is_empty() - && self.ssa_blocks.is_empty() - && self.calls.is_empty() - && self.results.is_empty() - && self.side_effects.is_empty() - } -} - -/// States for the `use_var`/`predecessors_lookup` state machine. -enum Call { - UseVar(Inst), - FinishPredecessorsLookup(Value, Block), -} - -/// The following methods are the API of the SSA builder. Here is how it should be used when -/// translating to Miden IR: -/// -/// - for each basic block, create a corresponding data for SSA construction with `declare_block`; -/// -/// - while traversing a basic block and translating instruction, use `def_var` and `use_var` -/// to record definitions and uses of variables, these methods will give you the corresponding -/// SSA values; -/// -/// - when all the instructions in a basic block have translated, the block is said _filled_ and -/// only then you can add it as a predecessor to other blocks with `declare_block_predecessor`; -/// -/// - when you have constructed all the predecessor to a basic block, -/// call `seal_block` on it with the `Function` that you are building. -/// -/// This API will give you the correct SSA values to use as arguments of your instructions, -/// as well as modify the jump instruction and `Block` parameters to account for the SSA -/// Phi functions. -/// -impl SSABuilder { - /// Declares a new definition of a variable in a given basic block. - pub fn def_var(&mut self, var: Variable, val: Value, block: Block) { - self.variables[var][block] = PackedOption::from(val); - } - - /// Declares a use of a variable in a given basic block. Returns the SSA value corresponding - /// to the current SSA definition of this variable and a list of newly created Blocks - /// - /// If the variable has never been defined in this blocks or recursively in its predecessors, - /// this method will silently create an initializer. You are - /// responsible for making sure that you initialize your variables. - pub fn use_var( - &mut self, - dfg: &mut DataFlowGraph, - var: Variable, - ty: Type, - block: Block, - ) -> (Value, SideEffects) { - debug_assert!(self.calls.is_empty()); - debug_assert!(self.results.is_empty()); - debug_assert!(self.side_effects.is_empty()); - - // Prepare the 'calls' and 'results' stacks for the state machine. - self.use_var_nonlocal(dfg, var, ty.clone(), block); - let value = self.run_state_machine(dfg, var, ty); - - let side_effects = mem::take(&mut self.side_effects); - (value, side_effects) - } - - /// Resolve the minimal SSA Value of `var` in `block` by traversing predecessors. - /// - /// This function sets up state for `run_state_machine()` but does not execute it. - fn use_var_nonlocal( - &mut self, - dfg: &mut DataFlowGraph, - var: Variable, - ty: Type, - mut block: Block, - ) { - // First, try Local Value Numbering (Algorithm 1 in the paper). - // If the variable already has a known Value in this block, use that. - if let Some(val) = self.variables[var][block].expand() { - self.results.push(val); - return; - } - - // Otherwise, use Global Value Numbering (Algorithm 2 in the paper). - // This resolves the Value with respect to its predecessors. - // Find the most recent definition of `var`, and the block the definition comes from. - let (val, from) = self.find_var(dfg, var, ty, block); - - // The `from` block returned from `find_var` is guaranteed to be on the path we follow by - // traversing only single-predecessor edges. It might be equal to `block` if there is no - // such path, but in that case `find_var` ensures that the variable is defined in this block - // by a new block parameter. It also might be somewhere in a cycle, but even then this loop - // will terminate the first time it encounters that block, rather than continuing around the - // cycle forever. - // - // Why is it okay to copy the definition to all intervening blocks? For the initial block, - // this may not be the final definition of this variable within this block, but if we've - // gotten here then we know there is no earlier definition in the block already. - // - // For the remaining blocks: Recall that a block is only allowed to be set as a predecessor - // after all its instructions have already been filled in, so when we follow a predecessor - // edge to a block, we know there will never be any more local variable definitions added to - // that block. We also know that `find_var` didn't find a definition for this variable in - // any of the blocks before `from`. - // - // So in either case there is no definition in these blocks yet and we can blindly set one. - let var_defs = &mut self.variables[var]; - while block != from { - debug_assert!(var_defs[block].is_none()); - var_defs[block] = PackedOption::from(val); - block = self.ssa_blocks[block].single_predecessor.unwrap(); - } - } - - /// Find the most recent definition of this variable, returning both the definition and the - /// block in which it was found. If we can't find a definition that's provably the right one for - /// all paths to the current block, then append a block parameter to some block and use that as - /// the definition. Either way, also arrange that the definition will be on the `results` stack - /// when `run_state_machine` is done processing the current step. - /// - /// If a block has exactly one predecessor, and the block is sealed so we know its predecessors - /// will never change, then its definition for this variable is the same as the definition from - /// that one predecessor. In this case it's easy to see that no block parameter is necessary, - /// but we need to look at the predecessor to see if a block parameter might be needed there. - /// That holds transitively across any chain of sealed blocks with exactly one predecessor each. - /// - /// This runs into a problem, though, if such a chain has a cycle: Blindly following a cyclic - /// chain that never defines this variable would lead to an infinite loop in the compiler. It - /// doesn't really matter what code we generate in that case. Since each block in the cycle has - /// exactly one predecessor, there's no way to enter the cycle from the function's entry block; - /// and since all blocks in the cycle are sealed, the entire cycle is permanently dead code. But - /// we still have to prevent the possibility of an infinite loop. - /// - /// To break cycles, we can pick any block within the cycle as the one where we'll add a block - /// parameter. It's convenient to pick the block at which we entered the cycle, because that's - /// the first place where we can detect that we just followed a cycle. Adding a block parameter - /// gives us a definition we can reuse throughout the rest of the cycle. - fn find_var( - &mut self, - dfg: &mut DataFlowGraph, - var: Variable, - ty: Type, - mut block: Block, - ) -> (Value, Block) { - // Try to find an existing definition along single-predecessor edges first. - self.visited.clear(); - let var_defs = &mut self.variables[var]; - while let Some(pred) = self.ssa_blocks[block].single_predecessor.expand() { - if !self.visited.insert(block) { - break; - } - block = pred; - if let Some(val) = var_defs[block].expand() { - self.results.push(val); - return (val, block); - } - } - - // We've promised to return the most recent block where `var` was defined, but we didn't - // find a usable definition. So create one. - let val = dfg.append_block_param(block, ty, SourceSpan::default()); - var_defs[block] = PackedOption::from(val); - - // Now every predecessor needs to pass its definition of this variable to the newly added - // block parameter. To do that we have to "recursively" call `use_var`, but there are two - // problems with doing that. First, we need to keep a fixed bound on stack depth, so we - // can't actually recurse; instead we defer to `run_state_machine`. Second, if we don't - // know all our predecessors yet, we have to defer this work until the block gets sealed. - match &mut self.ssa_blocks[block].sealed { - // Once all the `calls` added here complete, this leaves either `val` or an equivalent - // definition on the `results` stack. - Sealed::Yes => self.begin_predecessors_lookup(val, block), - Sealed::No { undef_variables } => { - undef_variables.push(var, &mut self.variable_pool); - self.results.push(val); - } - } - (val, block) - } - - /// Declares a new basic block to construct corresponding data for SSA construction. - /// No predecessors are declared here and the block is not sealed. - /// Predecessors have to be added with `declare_block_predecessor`. - pub fn declare_block(&mut self, block: Block) { - // Ensure the block exists so seal_one_block will see it even if no predecessors or - // variables get declared for this block. But don't assign anything to it: - // SecondaryMap automatically sets all blocks to `default()`. - let _ = &mut self.ssa_blocks[block]; - } - - /// Declares a new predecessor for a `Block` and record the branch instruction - /// of the predecessor that leads to it. - /// - /// The precedent `Block` must be filled before added as predecessor. - /// Note that you must provide no jump arguments to the branch - /// instruction when you create it since `SSABuilder` will fill them for you. - /// - /// Callers are expected to avoid adding the same predecessor more than once in the case - /// of a jump table. - pub fn declare_block_predecessor(&mut self, block: Block, inst: Inst) { - debug_assert!( - !self.is_sealed(block), - "you cannot add a predecessor to a sealed block" - ); - debug_assert!( - self.ssa_blocks[block] - .predecessors - .as_slice(&self.inst_pool) - .iter() - .all(|&branch| branch != inst), - "you have declared the same predecessor twice!" - ); - self.ssa_blocks[block] - .predecessors - .push(inst, &mut self.inst_pool); - } - - /// Remove a previously declared Block predecessor by giving a reference to the jump - /// instruction. Returns the basic block containing the instruction. - /// - /// Note: use only when you know what you are doing, this might break the SSA building problem - pub fn remove_block_predecessor(&mut self, block: Block, inst: Inst) { - debug_assert!(!self.is_sealed(block)); - let data = &mut self.ssa_blocks[block]; - let pred = data - .predecessors - .as_slice(&self.inst_pool) - .iter() - .position(|&branch| branch == inst) - .expect("the predecessor you are trying to remove is not declared"); - data.predecessors.swap_remove(pred, &mut self.inst_pool); - } - - /// Completes the global value numbering for a `Block`, all of its predecessors having been - /// already sealed. - /// - /// This method modifies the function's `Layout` by adding arguments to the `Block`s to - /// take into account the Phi function placed by the SSA algorithm. - /// - /// Returns the list of newly created blocks for critical edge splitting. - pub fn seal_block(&mut self, block: Block, dfg: &mut DataFlowGraph) -> SideEffects { - debug_assert!( - !self.is_sealed(block), - "Attempting to seal {} which is already sealed.", - block - ); - self.seal_one_block(block, dfg); - mem::take(&mut self.side_effects) - } - - /// Helper function for `seal_block` - fn seal_one_block(&mut self, block: Block, dfg: &mut DataFlowGraph) { - // For each undef var we look up values in the predecessors and create a block parameter - // only if necessary. - let mut undef_variables = - match mem::replace(&mut self.ssa_blocks[block].sealed, Sealed::Yes) { - Sealed::No { undef_variables } => undef_variables, - Sealed::Yes => return, - }; - let ssa_params = undef_variables.len(&self.variable_pool); - - let predecessors = self.predecessors(block); - if predecessors.len() == 1 { - let pred = dfg.insts[predecessors[0]].block; - self.ssa_blocks[block].single_predecessor = PackedOption::from(pred); - } - - // Note that begin_predecessors_lookup requires visiting these variables in the same order - // that they were defined by find_var, because it appends arguments to the jump instructions - // in all the predecessor blocks one variable at a time. - for idx in 0..ssa_params { - let var = undef_variables.get(idx, &self.variable_pool).unwrap(); - - // We need the temporary Value that was assigned to this Variable. If that Value shows - // up as a result from any of our predecessors, then it never got assigned on the loop - // through that block. We get the value from the next block param, where it was first - // allocated in find_var. - let block_params = dfg.block_params(block); - - // On each iteration through this loop, there are (ssa_params - idx) undefined variables - // left to process. Previous iterations through the loop may have removed earlier block - // parameters, but the last (ssa_params - idx) block parameters always correspond to the - // remaining undefined variables. So index from the end of the current block params. - let val = block_params[block_params.len() - (ssa_params - idx)]; - - debug_assert!(self.calls.is_empty()); - debug_assert!(self.results.is_empty()); - // self.side_effects may be non-empty here so that callers can - // accumulate side effects over multiple calls. - self.begin_predecessors_lookup(val, block); - self.run_state_machine(dfg, var, dfg.value_type(val).clone()); - } - - undef_variables.clear(&mut self.variable_pool); - } - - /// Given the local SSA Value of a Variable in a Block, perform a recursive lookup on - /// predecessors to determine if it is redundant with another Value earlier in the CFG. - /// - /// If such a Value exists and is redundant, the local Value is replaced by the - /// corresponding non-local Value. If the original Value was a Block parameter, - /// the parameter may be removed if redundant. Parameters are placed eagerly by callers - /// to avoid infinite loops when looking up a Value for a Block that is in a CFG loop. - /// - /// Doing this lookup for each Value in each Block preserves SSA form during construction. - /// - /// ## Arguments - /// - /// `sentinel` is a dummy Block parameter inserted by `use_var_nonlocal()`. - /// Its purpose is to allow detection of CFG cycles while traversing predecessors. - fn begin_predecessors_lookup(&mut self, sentinel: Value, dest_block: Block) { - self.calls - .push(Call::FinishPredecessorsLookup(sentinel, dest_block)); - // Iterate over the predecessors. - self.calls.extend( - self.ssa_blocks[dest_block] - .predecessors - .as_slice(&self.inst_pool) - .iter() - .rev() - .copied() - .map(Call::UseVar), - ); - } - - /// Examine the values from the predecessors and compute a result value, creating - /// block parameters as needed. - fn finish_predecessors_lookup( - &mut self, - dfg: &mut DataFlowGraph, - sentinel: Value, - dest_block: Block, - ) -> Value { - // Determine how many predecessors are yielding unique, non-temporary Values. - let num_predecessors = self.predecessors(dest_block).len(); - // When this `Drain` is dropped, these elements will get truncated. - let results = self.results.drain(self.results.len() - num_predecessors..); - // Keep the block argument. - let mut preds = self.ssa_blocks[dest_block].predecessors; - for (idx, &val) in results.as_slice().iter().enumerate() { - let pred = preds.get_mut(idx, &mut self.inst_pool).unwrap(); - let branch = *pred; - assert!( - dfg.insts[branch].opcode().is_branch(), - "you have declared a non-branch instruction as a predecessor to a block!" - ); - dfg.append_branch_destination_argument(branch, dest_block, val); - } - sentinel - } - - /// Returns the list of `Block`s that have been declared as predecessors of the argument. - fn predecessors(&self, block: Block) -> &[Inst] { - self.ssa_blocks[block] - .predecessors - .as_slice(&self.inst_pool) - } - - /// Returns whether the given Block has any predecessor or not. - pub fn has_any_predecessors(&self, block: Block) -> bool { - !self.predecessors(block).is_empty() - } - - /// Returns `true` if and only if `seal_block` has been called on the argument. - pub fn is_sealed(&self, block: Block) -> bool { - matches!(self.ssa_blocks[block].sealed, Sealed::Yes) - } - - /// The main algorithm is naturally recursive: when there's a `use_var` in a - /// block with no corresponding local defs, it recurses and performs a - /// `use_var` in each predecessor. To avoid risking running out of callstack - /// space, we keep an explicit stack and use a small state machine rather - /// than literal recursion. - fn run_state_machine(&mut self, func: &mut DataFlowGraph, var: Variable, ty: Type) -> Value { - // Process the calls scheduled in `self.calls` until it is empty. - while let Some(call) = self.calls.pop() { - match call { - Call::UseVar(branch) => { - let block = func.insts[branch].block; - self.use_var_nonlocal(func, var, ty.clone(), block); - } - Call::FinishPredecessorsLookup(sentinel, dest_block) => { - let val = self.finish_predecessors_lookup(func, sentinel, dest_block); - self.results.push(val); - } - } - } - debug_assert_eq!(self.results.len(), 1); - self.results.pop().unwrap() - } -} diff --git a/frontend-wasm/src/test_utils.rs b/frontend-wasm/src/test_utils.rs deleted file mode 100644 index 63b96d076..000000000 --- a/frontend-wasm/src/test_utils.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::sync::Arc; - -use miden_diagnostics::term::termcolor::ColorChoice; -use miden_diagnostics::CodeMap; -use miden_diagnostics::DiagnosticsConfig; -use miden_diagnostics::DiagnosticsHandler; -use miden_diagnostics::Emitter; -use miden_diagnostics::NullEmitter; -use miden_diagnostics::Verbosity; - -pub fn default_emitter(verbosity: Verbosity, color: ColorChoice) -> Arc { - match verbosity { - _ => Arc::new(NullEmitter::new(color)), - } -} - -pub fn test_diagnostics() -> DiagnosticsHandler { - let codemap = Arc::new(CodeMap::new()); - let diagnostics = DiagnosticsHandler::new( - DiagnosticsConfig { - verbosity: Verbosity::Debug, - warnings_as_errors: false, - no_warn: false, - display: Default::default(), - }, - codemap, - default_emitter(Verbosity::Debug, ColorChoice::Auto), - ); - diagnostics -} diff --git a/frontend-wasm/src/translation_utils.rs b/frontend-wasm/src/translation_utils.rs deleted file mode 100644 index a60d01c26..000000000 --- a/frontend-wasm/src/translation_utils.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Helper functions and structures for the translation. - -use miden_diagnostics::SourceSpan; -use miden_hir::{AbiParam, CallConv, InstBuilder, Linkage, Signature, Value}; -use miden_hir_type::{FunctionType, Type}; - -use crate::{error::WasmResult, module::function_builder_ext::FunctionBuilderExt, WasmError}; - -/// Represents the possible sizes in bytes of the discriminant of a variant type in the component model -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum DiscriminantSize { - /// 8-bit discriminant - Size1, - /// 16-bit discriminant - Size2, - /// 32-bit discriminant - Size4, -} - -impl DiscriminantSize { - /// Calculate the size of discriminant needed to represent a variant with the specified number of cases. - pub const fn from_count(count: usize) -> Option { - if count <= 0xFF { - Some(Self::Size1) - } else if count <= 0xFFFF { - Some(Self::Size2) - } else if count <= 0xFFFF_FFFF { - Some(Self::Size4) - } else { - None - } - } - - /// Returns the size, in bytes, of this discriminant - pub const fn byte_size(&self) -> u32 { - match self { - DiscriminantSize::Size1 => 1, - DiscriminantSize::Size2 => 2, - DiscriminantSize::Size4 => 4, - } - } -} - -impl From for u32 { - /// Size of the discriminant as a `u32` - fn from(size: DiscriminantSize) -> u32 { - size.byte_size() - } -} - -impl From for usize { - /// Size of the discriminant as a `usize` - fn from(size: DiscriminantSize) -> usize { - match size { - DiscriminantSize::Size1 => 1, - DiscriminantSize::Size2 => 2, - DiscriminantSize::Size4 => 4, - } - } -} - -/// Represents the number of bytes required to store a flags value in the component model -pub enum FlagsSize { - /// There are no flags - Size0, - /// Flags can fit in a u8 - Size1, - /// Flags can fit in a u16 - Size2, - /// Flags can fit in a specified number of u32 fields - Size4Plus(u8), -} - -impl FlagsSize { - /// Calculate the size needed to represent a value with the specified number of flags. - pub const fn from_count(count: usize) -> FlagsSize { - if count == 0 { - FlagsSize::Size0 - } else if count <= 8 { - FlagsSize::Size1 - } else if count <= 16 { - FlagsSize::Size2 - } else { - let amt = ceiling_divide(count, 32); - if amt > (u8::MAX as usize) { - panic!("too many flags"); - } - FlagsSize::Size4Plus(amt as u8) - } - } -} - -/// Divide `n` by `d`, rounding up in the case of a non-zero remainder. -const fn ceiling_divide(n: usize, d: usize) -> usize { - (n + d - 1) / d -} - -/// Emit instructions to produce a zero value in the given type. -pub fn emit_zero(ty: &Type, builder: &mut FunctionBuilderExt) -> WasmResult { - Ok(match ty { - Type::I1 => builder.ins().i1(false, SourceSpan::default()), - Type::I8 => builder.ins().i8(0, SourceSpan::default()), - Type::I16 => builder.ins().i16(0, SourceSpan::default()), - Type::I32 => builder.ins().i32(0, SourceSpan::default()), - Type::I64 => builder.ins().i64(0, SourceSpan::default()), - Type::U8 => builder.ins().u8(0, SourceSpan::default()), - Type::U16 => builder.ins().u16(0, SourceSpan::default()), - Type::U32 => builder.ins().u32(0, SourceSpan::default()), - Type::U64 => builder.ins().u64(0, SourceSpan::default()), - Type::F64 => builder.ins().f64(0.0, SourceSpan::default()), - Type::Felt => builder.ins().felt(0u64.into(), SourceSpan::default()), - Type::I128 - | Type::U128 - | Type::U256 - | Type::Ptr(_) - | Type::NativePtr(_, _) - | Type::Struct(_) - | Type::Array(_, _) - | Type::Unknown - | Type::Unit - | Type::Never => { - return Err(WasmError::Unsupported(format!( - "cannot emit zero value for type: {:?}", - ty - ))); - } - }) -} - -pub fn sig_from_funct_type( - func_type: &FunctionType, - call_conv: CallConv, - linkage: Linkage, -) -> Signature { - Signature { - params: func_type - .params - .iter() - .map(|ty| AbiParam::new(ty.clone())) - .collect(), - results: func_type - .results - .iter() - .map(|ty| AbiParam::new(ty.clone())) - .collect(), - cc: call_conv, - linkage, - } -} diff --git a/frontend-wasm/tests/expected/add.hir b/frontend-wasm/tests/expected/add.hir deleted file mode 100644 index 062e509cd..000000000 --- a/frontend-wasm/tests/expected/add.hir +++ /dev/null @@ -1,21 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn __main() -> i32 { -block0: - v1 = const.i32 1 : i32; - v2 = const.i32 2 : i32; - v3 = call noname::add(v1, v2) : i32; - ret v3; -} - -pub fn add(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = add.wrapping v1, v0 : i32; - ret v3; -} diff --git a/frontend-wasm/tests/expected/add.wat b/frontend-wasm/tests/expected/add.wat deleted file mode 100644 index e66f8e2c0..000000000 --- a/frontend-wasm/tests/expected/add.wat +++ /dev/null @@ -1,23 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (type (;1;) (func (result i32))) - (func $add (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.add - ) - (func $__main (;1;) (type 1) (result i32) - i32.const 1 - i32.const 2 - call $add - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "add" (func $add)) - (export "__main" (func $__main)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/frontend-wasm/tests/expected/array.hir b/frontend-wasm/tests/expected/array.hir deleted file mode 100644 index ea3d04799..000000000 --- a/frontend-wasm/tests/expected/array.hir +++ /dev/null @@ -1,55 +0,0 @@ -module noname - -const $0 = 0x00100000; -const $1 = 0x00100028; -const $2 = 0x00100030; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $1 { id = 1 }; -global external @gv2 : i32 = $2 { id = 2 }; - -pub fn __main() -> i32 { -block0: - v1 = const.i32 1048576 : i32; - v2 = const.i32 5 : i32; - v3 = call noname::sum_arr(v1, v2) : i32; - v4 = const.i32 1048596 : i32; - v5 = const.i32 5 : i32; - v6 = call noname::sum_arr(v4, v5) : i32; - v7 = add.wrapping v3, v6 : i32; - ret v7; -} - -pub fn sum_arr(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 0 : i32; - v4 = const.i32 0 : i32; - v5 = eq v1, 0 : i1; - v6 = cast v5 : i32; - v7 = neq v6, 0 : i1; - condbr v7, block7, block3; - -block7: - ret v4; - -block3: - br block4(v0, v4, v1); - -block4(v8: i32, v12: i32, v16: i32): - v9 = cast v8 : u32; - v10 = inttoptr v9 : *mut i32; - v11 = load v10 : i32; - v13 = add.wrapping v11, v12 : i32; - v14 = const.i32 4 : i32; - v15 = add.wrapping v8, v14 : i32; - v17 = const.i32 -1 : i32; - v18 = add.wrapping v16, v17 : i32; - v19 = neq v18, 0 : i1; - condbr v19, block8, block6; - -block8: - br block4(v15, v13, v18); - -block6: - ret v13; -} diff --git a/frontend-wasm/tests/expected/array.wat b/frontend-wasm/tests/expected/array.wat deleted file mode 100644 index 6370ac74d..000000000 --- a/frontend-wasm/tests/expected/array.wat +++ /dev/null @@ -1,50 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (type (;1;) (func (result i32))) - (func $sum_arr (;0;) (type 0) (param i32 i32) (result i32) - (local i32) - i32.const 0 - local.set 2 - block ;; label = @1 - local.get 1 - i32.eqz - br_if 0 (;@1;) - loop ;; label = @2 - local.get 0 - i32.load - local.get 2 - i32.add - local.set 2 - local.get 0 - i32.const 4 - i32.add - local.set 0 - local.get 1 - i32.const -1 - i32.add - local.tee 1 - br_if 0 (;@2;) - end - end - local.get 2 - ) - (func $__main (;1;) (type 1) (result i32) - i32.const 1048576 - i32.const 5 - call $sum_arr - i32.const 1048596 - i32.const 5 - call $sum_arr - i32.add - ) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048616) - (global (;2;) i32 i32.const 1048624) - (export "memory" (memory 0)) - (export "sum_arr" (func $sum_arr)) - (export "__main" (func $__main)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) - (data $.rodata (;0;) (i32.const 1048576) "\01\00\00\00\02\00\00\00\03\00\00\00\04\00\00\00\05\00\00\00\06\00\00\00\07\00\00\00\08\00\00\00\09\00\00\00\0a\00\00\00") -) \ No newline at end of file diff --git a/frontend-wasm/tests/expected/dlmalloc.hir b/frontend-wasm/tests/expected/dlmalloc.hir deleted file mode 100644 index db8e9040b..000000000 --- a/frontend-wasm/tests/expected/dlmalloc.hir +++ /dev/null @@ -1,4720 +0,0 @@ -module noname - -const $0 = 0x00100000; -const $1 = 0x001001c8; -const $2 = 0x001001d0; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $1 { id = 1 }; -global external @gv2 : i32 = $2 { id = 2 }; - -pub fn dlmalloc::dlmalloc::Dlmalloc::dispose_chunk(i32, i32, i32) { -block0(v0: i32, v1: i32, v2: i32): - v3 = const.i32 0 : i32; - v4 = add.wrapping v1, v2 : i32; - v5 = cast v1 : u32; - v6 = add.checked v5, 4 : u32; - v7 = inttoptr v6 : *mut i32; - v8 = load v7 : i32; - v9 = const.i32 1 : i32; - v10 = band v8, v9 : i32; - v11 = neq v10, 0 : i1; - condbr v11, block4(v2, v1), block5; - -block1: - ret; - -block2: - v273 = cast v100 : u32; - v274 = add.checked v273, 416 : u32; - v275 = inttoptr v274 : *mut i32; - v276 = load v275 : i32; - v277 = add.wrapping v276, v117 : i32; - v278 = const.i32 1 : i32; - v279 = bor v277, v278 : i32; - v280 = cast v158 : u32; - v281 = add.checked v280, 4 : u32; - v282 = inttoptr v281 : *mut i32; - store v282, v279; - v283 = cast v100 : u32; - v284 = add.checked v283, 424 : u32; - v285 = inttoptr v284 : *mut i32; - store v285, v158; - v286 = cast v100 : u32; - v287 = add.checked v286, 416 : u32; - v288 = inttoptr v287 : *mut i32; - store v288, v277; - v289 = add.wrapping v158, v277 : i32; - v290 = cast v289 : u32; - v291 = inttoptr v290 : *mut i32; - store v291, v277; - br block1; - -block3: - ret; - -block4(v117: i32, v158: i32): - v93 = cast v4 : u32; - v94 = add.checked v93, 4 : u32; - v95 = inttoptr v94 : *mut i32; - v96 = load v95 : i32; - v97 = const.i32 2 : i32; - v98 = band v96, v97 : i32; - v99 = neq v98, 0 : i1; - condbr v99, block16, block17; - -block5: - v12 = const.i32 3 : i32; - v13 = band v8, v12 : i32; - v14 = eq v13, 0 : i1; - v15 = cast v14 : i32; - v16 = neq v15, 0 : i1; - condbr v16, block3, block6; - -block6: - v17 = cast v1 : u32; - v18 = inttoptr v17 : *mut i32; - v19 = load v18 : i32; - v20 = add.wrapping v19, v2 : i32; - v21 = sub.wrapping v1, v19 : i32; - v22 = cast v0 : u32; - v23 = add.checked v22, 424 : u32; - v24 = inttoptr v23 : *mut i32; - v25 = load v24 : i32; - v26 = neq v21, v25 : i1; - v27 = cast v26 : i32; - v28 = neq v27, 0 : i1; - condbr v28, block7, block8; - -block7: - v54 = const.i32 256 : i32; - v55 = cast v19 : u32; - v56 = cast v54 : u32; - v57 = lt v55, v56 : i1; - v58 = cast v57 : i32; - v59 = neq v58, 0 : i1; - condbr v59, block10, block11; - -block8: - v29 = cast v4 : u32; - v30 = add.checked v29, 4 : u32; - v31 = inttoptr v30 : *mut i32; - v32 = load v31 : i32; - v33 = const.i32 3 : i32; - v34 = band v32, v33 : i32; - v35 = const.i32 3 : i32; - v36 = neq v34, v35 : i1; - v37 = cast v36 : i32; - v38 = neq v37, 0 : i1; - condbr v38, block4(v20, v21), block9; - -block9: - v39 = const.i32 -2 : i32; - v40 = band v32, v39 : i32; - v41 = cast v4 : u32; - v42 = add.checked v41, 4 : u32; - v43 = inttoptr v42 : *mut i32; - store v43, v40; - v44 = const.i32 1 : i32; - v45 = bor v20, v44 : i32; - v46 = cast v21 : u32; - v47 = add.checked v46, 4 : u32; - v48 = inttoptr v47 : *mut i32; - store v48, v45; - v49 = cast v0 : u32; - v50 = add.checked v49, 416 : u32; - v51 = inttoptr v50 : *mut i32; - store v51, v20; - v52 = cast v4 : u32; - v53 = inttoptr v52 : *mut i32; - store v53, v20; - ret; - -block10: - v60 = cast v21 : u32; - v61 = add.checked v60, 12 : u32; - v62 = inttoptr v61 : *mut i32; - v63 = load v62 : i32; - v64 = cast v21 : u32; - v65 = add.checked v64, 8 : u32; - v66 = inttoptr v65 : *mut i32; - v67 = load v66 : i32; - v68 = eq v63, v67 : i1; - v69 = cast v68 : i32; - v70 = neq v69, 0 : i1; - condbr v70, block12, block13; - -block11: - call noname::dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(v0, v21); - br block4(v20, v21); - -block12: - v77 = cast v0 : u32; - v78 = add.checked v77, 408 : u32; - v79 = inttoptr v78 : *mut i32; - v80 = load v79 : i32; - v81 = const.i32 -2 : i32; - v82 = const.i32 3 : i32; - v83 = cast v19 : u32; - v84 = cast v82 : u32; - v85 = shr.wrapping v83, v84 : u32; - v86 = cast v85 : i32; - v87 = rotl v81, v86 : i32; - v88 = band v80, v87 : i32; - v89 = cast v0 : u32; - v90 = add.checked v89, 408 : u32; - v91 = inttoptr v90 : *mut i32; - store v91, v88; - br block4(v20, v21); - -block13: - v71 = cast v67 : u32; - v72 = add.checked v71, 12 : u32; - v73 = inttoptr v72 : *mut i32; - store v73, v63; - v74 = cast v63 : u32; - v75 = add.checked v74, 8 : u32; - v76 = inttoptr v75 : *mut i32; - store v76, v67; - br block4(v20, v21); - -block14: - v242 = cast v100 : u32; - v243 = add.checked v242, 428 : u32; - v244 = inttoptr v243 : *mut i32; - store v244, v158; - v245 = cast v100 : u32; - v246 = add.checked v245, 420 : u32; - v247 = inttoptr v246 : *mut i32; - v248 = load v247 : i32; - v249 = add.wrapping v248, v117 : i32; - v250 = cast v100 : u32; - v251 = add.checked v250, 420 : u32; - v252 = inttoptr v251 : *mut i32; - store v252, v249; - v253 = const.i32 1 : i32; - v254 = bor v249, v253 : i32; - v255 = cast v158 : u32; - v256 = add.checked v255, 4 : u32; - v257 = inttoptr v256 : *mut i32; - store v257, v254; - v258 = cast v100 : u32; - v259 = add.checked v258, 424 : u32; - v260 = inttoptr v259 : *mut i32; - v261 = load v260 : i32; - v262 = neq v158, v261 : i1; - v263 = cast v262 : i32; - v264 = neq v263, 0 : i1; - condbr v264, block3, block31; - -block15(v192: i32, v199: i32, v200: i32): - v193 = const.i32 256 : i32; - v194 = cast v192 : u32; - v195 = cast v193 : u32; - v196 = lt v194, v195 : i1; - v197 = cast v196 : i32; - v198 = neq v197, 0 : i1; - condbr v198, block26, block27; - -block16: - v179 = const.i32 -2 : i32; - v180 = band v96, v179 : i32; - v181 = cast v92 : u32; - v182 = add.checked v181, 4 : u32; - v183 = inttoptr v182 : *mut i32; - store v183, v180; - v184 = const.i32 1 : i32; - v185 = bor v117, v184 : i32; - v186 = cast v158 : u32; - v187 = add.checked v186, 4 : u32; - v188 = inttoptr v187 : *mut i32; - store v188, v185; - v189 = add.wrapping v158, v117 : i32; - v190 = cast v189 : u32; - v191 = inttoptr v190 : *mut i32; - store v191, v117; - br block15(v117, v100, v158); - -block17: - v101 = cast v0 : u32; - v102 = add.checked v101, 428 : u32; - v103 = inttoptr v102 : *mut i32; - v104 = load v103 : i32; - v105 = eq v92, v104 : i1; - v106 = cast v105 : i32; - v107 = neq v106, 0 : i1; - condbr v107, block14, block18; - -block18: - v108 = cast v100 : u32; - v109 = add.checked v108, 424 : u32; - v110 = inttoptr v109 : *mut i32; - v111 = load v110 : i32; - v112 = eq v92, v111 : i1; - v113 = cast v112 : i32; - v114 = neq v113, 0 : i1; - condbr v114, block2, block19; - -block19: - v115 = const.i32 -8 : i32; - v116 = band v96, v115 : i32; - v118 = add.wrapping v116, v117 : i32; - v119 = const.i32 256 : i32; - v120 = cast v116 : u32; - v121 = cast v119 : u32; - v122 = lt v120, v121 : i1; - v123 = cast v122 : i32; - v124 = neq v123, 0 : i1; - condbr v124, block21, block22; - -block20: - v160 = const.i32 1 : i32; - v161 = bor v118, v160 : i32; - v162 = cast v158 : u32; - v163 = add.checked v162, 4 : u32; - v164 = inttoptr v163 : *mut i32; - store v164, v161; - v165 = add.wrapping v157, v159 : i32; - v166 = cast v165 : u32; - v167 = inttoptr v166 : *mut i32; - store v167, v159; - v169 = cast v100 : u32; - v170 = add.checked v169, 424 : u32; - v171 = inttoptr v170 : *mut i32; - v172 = load v171 : i32; - v173 = neq v157, v172 : i1; - v174 = cast v173 : i32; - v175 = neq v174, 0 : i1; - condbr v175, block15(v159, v168, v157), block25; - -block21: - v125 = cast v92 : u32; - v126 = add.checked v125, 12 : u32; - v127 = inttoptr v126 : *mut i32; - v128 = load v127 : i32; - v129 = cast v92 : u32; - v130 = add.checked v129, 8 : u32; - v131 = inttoptr v130 : *mut i32; - v132 = load v131 : i32; - v133 = eq v128, v132 : i1; - v134 = cast v133 : i32; - v135 = neq v134, 0 : i1; - condbr v135, block23, block24; - -block22: - call noname::dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(v100, v92); - br block20; - -block23: - v142 = cast v100 : u32; - v143 = add.checked v142, 408 : u32; - v144 = inttoptr v143 : *mut i32; - v145 = load v144 : i32; - v146 = const.i32 -2 : i32; - v147 = const.i32 3 : i32; - v148 = cast v96 : u32; - v149 = cast v147 : u32; - v150 = shr.wrapping v148, v149 : u32; - v151 = cast v150 : i32; - v152 = rotl v146, v151 : i32; - v153 = band v145, v152 : i32; - v154 = cast v100 : u32; - v155 = add.checked v154, 408 : u32; - v156 = inttoptr v155 : *mut i32; - store v156, v153; - br block20; - -block24: - v136 = cast v132 : u32; - v137 = add.checked v136, 12 : u32; - v138 = inttoptr v137 : *mut i32; - store v138, v128; - v139 = cast v128 : u32; - v140 = add.checked v139, 8 : u32; - v141 = inttoptr v140 : *mut i32; - store v141, v132; - br block20; - -block25: - v176 = cast v168 : u32; - v177 = add.checked v176, 416 : u32; - v178 = inttoptr v177 : *mut i32; - store v178, v159; - ret; - -block26: - v201 = const.i32 -8 : i32; - v202 = band v192, v201 : i32; - v203 = add.wrapping v199, v202 : i32; - v204 = const.i32 144 : i32; - v205 = add.wrapping v203, v204 : i32; - v206 = cast v199 : u32; - v207 = add.checked v206, 408 : u32; - v208 = inttoptr v207 : *mut i32; - v209 = load v208 : i32; - v210 = const.i32 1 : i32; - v211 = const.i32 3 : i32; - v212 = cast v192 : u32; - v213 = cast v211 : u32; - v214 = shr.wrapping v212, v213 : u32; - v215 = cast v214 : i32; - v216 = shl.wrapping v210, v215 : i32; - v217 = band v209, v216 : i32; - v218 = neq v217, 0 : i1; - condbr v218, block29, block30; - -block27: - call noname::dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk(v199, v200, v192); - ret; - -block28(v232: i32): - v229 = cast v205 : u32; - v230 = add.checked v229, 8 : u32; - v231 = inttoptr v230 : *mut i32; - store v231, v200; - v233 = cast v232 : u32; - v234 = add.checked v233, 12 : u32; - v235 = inttoptr v234 : *mut i32; - store v235, v228; - v236 = cast v228 : u32; - v237 = add.checked v236, 12 : u32; - v238 = inttoptr v237 : *mut i32; - store v238, v227; - v239 = cast v228 : u32; - v240 = add.checked v239, 8 : u32; - v241 = inttoptr v240 : *mut i32; - store v241, v232; - ret; - -block29: - v223 = cast v205 : u32; - v224 = add.checked v223, 8 : u32; - v225 = inttoptr v224 : *mut i32; - v226 = load v225 : i32; - br block28(v226); - -block30: - v219 = bor v209, v216 : i32; - v220 = cast v199 : u32; - v221 = add.checked v220, 408 : u32; - v222 = inttoptr v221 : *mut i32; - store v222, v219; - br block28(v205); - -block31: - v265 = const.i32 0 : i32; - v266 = cast v100 : u32; - v267 = add.checked v266, 416 : u32; - v268 = inttoptr v267 : *mut i32; - store v268, v265; - v269 = const.i32 0 : i32; - v270 = cast v100 : u32; - v271 = add.checked v270, 424 : u32; - v272 = inttoptr v271 : *mut i32; - store v272, v269; - br block3; -} - -pub fn dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(i32, i32) { -block0(v0: i32, v1: i32): - v2 = const.i32 0 : i32; - v3 = cast v1 : u32; - v4 = add.checked v3, 24 : u32; - v5 = inttoptr v4 : *mut i32; - v6 = load v5 : i32; - v7 = cast v1 : u32; - v8 = add.checked v7, 12 : u32; - v9 = inttoptr v8 : *mut i32; - v10 = load v9 : i32; - v11 = neq v10, v1 : i1; - v12 = cast v11 : i32; - v13 = neq v12, 0 : i1; - condbr v13, block4, block5; - -block1: - ret; - -block2(v99: i32): - v68 = eq v6, 0 : i1; - v69 = cast v68 : i32; - v70 = neq v69, 0 : i1; - condbr v70, block10, block11; - -block3: - v39 = const.i32 16 : i32; - v40 = add.wrapping v1, v39 : i32; - v41 = neq v20, 0 : i1; - v42 = select v41, v17, v40 : i32; - br block7(v42, v26); - -block4: - v29 = cast v1 : u32; - v30 = add.checked v29, 8 : u32; - v31 = inttoptr v30 : *mut i32; - v32 = load v31 : i32; - v33 = cast v32 : u32; - v34 = add.checked v33, 12 : u32; - v35 = inttoptr v34 : *mut i32; - store v35, v10; - v36 = cast v10 : u32; - v37 = add.checked v36, 8 : u32; - v38 = inttoptr v37 : *mut i32; - store v38, v32; - br block2(v10); - -block5: - v14 = const.i32 20 : i32; - v15 = const.i32 16 : i32; - v16 = const.i32 20 : i32; - v17 = add.wrapping v1, v16 : i32; - v18 = cast v17 : u32; - v19 = inttoptr v18 : *mut i32; - v20 = load v19 : i32; - v21 = neq v20, 0 : i1; - v22 = select v21, v14, v15 : i32; - v23 = add.wrapping v1, v22 : i32; - v24 = cast v23 : u32; - v25 = inttoptr v24 : *mut i32; - v26 = load v25 : i32; - v27 = neq v26, 0 : i1; - condbr v27, block3, block6; - -block6: - v28 = const.i32 0 : i32; - br block2(v28); - -block7(v43: i32, v44: i32): - v45 = const.i32 20 : i32; - v46 = add.wrapping v44, v45 : i32; - v47 = const.i32 16 : i32; - v48 = add.wrapping v44, v47 : i32; - v49 = cast v46 : u32; - v50 = inttoptr v49 : *mut i32; - v51 = load v50 : i32; - v52 = neq v51, 0 : i1; - v53 = select v52, v46, v48 : i32; - v54 = const.i32 20 : i32; - v55 = const.i32 16 : i32; - v56 = neq v51, 0 : i1; - v57 = select v56, v54, v55 : i32; - v58 = add.wrapping v44, v57 : i32; - v59 = cast v58 : u32; - v60 = inttoptr v59 : *mut i32; - v61 = load v60 : i32; - v62 = neq v61, 0 : i1; - condbr v62, block7(v53, v61), block9; - -block8: - v63 = const.i32 0 : i32; - v64 = cast v43 : u32; - v65 = inttoptr v64 : *mut i32; - store v65, v63; - br block2(v44); - -block9: - br block8; - -block10: - br block1; - -block11: - v75 = cast v1 : u32; - v76 = add.checked v75, 28 : u32; - v77 = inttoptr v76 : *mut i32; - v78 = load v77 : i32; - v79 = const.i32 2 : i32; - v80 = shl.wrapping v78, v79 : i32; - v81 = add.wrapping v0, v80 : i32; - v82 = cast v81 : u32; - v83 = inttoptr v82 : *mut i32; - v84 = load v83 : i32; - v85 = eq v84, v73 : i1; - v86 = cast v85 : i32; - v87 = neq v86, 0 : i1; - condbr v87, block13, block14; - -block12: - v118 = cast v99 : u32; - v119 = add.checked v118, 24 : u32; - v120 = inttoptr v119 : *mut i32; - store v120, v66; - v122 = cast v73 : u32; - v123 = add.checked v122, 16 : u32; - v124 = inttoptr v123 : *mut i32; - v125 = load v124 : i32; - v126 = eq v125, 0 : i1; - v127 = cast v126 : i32; - v128 = neq v127, 0 : i1; - condbr v128, block17, block18; - -block13: - v103 = cast v81 : u32; - v104 = inttoptr v103 : *mut i32; - store v104, v99; - v105 = neq v99, 0 : i1; - condbr v105, block12, block16; - -block14: - v88 = const.i32 16 : i32; - v89 = const.i32 20 : i32; - v90 = cast v66 : u32; - v91 = add.checked v90, 16 : u32; - v92 = inttoptr v91 : *mut i32; - v93 = load v92 : i32; - v94 = eq v93, v73 : i1; - v95 = cast v94 : i32; - v96 = neq v95, 0 : i1; - v97 = select v96, v88, v89 : i32; - v98 = add.wrapping v66, v97 : i32; - v100 = cast v98 : u32; - v101 = inttoptr v100 : *mut i32; - store v101, v99; - v102 = neq v99, 0 : i1; - condbr v102, block12, block15; - -block15: - br block10; - -block16: - v106 = cast v71 : u32; - v107 = add.checked v106, 412 : u32; - v108 = inttoptr v107 : *mut i32; - v109 = load v108 : i32; - v110 = const.i32 -2 : i32; - v111 = rotl v110, v78 : i32; - v112 = band v109, v111 : i32; - v113 = cast v71 : u32; - v114 = add.checked v113, 412 : u32; - v115 = inttoptr v114 : *mut i32; - store v115, v112; - ret; - -block17: - v136 = const.i32 20 : i32; - v137 = add.wrapping v121, v136 : i32; - v138 = cast v137 : u32; - v139 = inttoptr v138 : *mut i32; - v140 = load v139 : i32; - v141 = eq v140, 0 : i1; - v142 = cast v141 : i32; - v143 = neq v142, 0 : i1; - condbr v143, block10, block19; - -block18: - v129 = cast v116 : u32; - v130 = add.checked v129, 16 : u32; - v131 = inttoptr v130 : *mut i32; - store v131, v125; - v132 = cast v125 : u32; - v133 = add.checked v132, 24 : u32; - v134 = inttoptr v133 : *mut i32; - store v134, v116; - br block17; - -block19: - v145 = const.i32 20 : i32; - v146 = add.wrapping v116, v145 : i32; - v147 = cast v146 : u32; - v148 = inttoptr v147 : *mut i32; - store v148, v140; - v149 = cast v140 : u32; - v150 = add.checked v149, 24 : u32; - v151 = inttoptr v150 : *mut i32; - store v151, v144; - ret; -} - -pub fn dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk(i32, i32, i32) { -block0(v0: i32, v1: i32, v2: i32): - v3 = const.i32 0 : i32; - v4 = const.i32 0 : i32; - v5 = const.i32 256 : i32; - v6 = cast v2 : u32; - v7 = cast v5 : u32; - v8 = lt v6, v7 : i1; - v9 = cast v8 : i32; - v10 = neq v9, 0 : i1; - condbr v10, block2(v4), block3; - -block1: - ret; - -block2(v42: i32): - v38 = const.i64 0 : i64; - v39 = cast v1 : u32; - v40 = add.checked v39, 16 : u32; - v41 = inttoptr v40 : *mut i64; - store v41, v38; - v43 = cast v37 : u32; - v44 = add.checked v43, 28 : u32; - v45 = inttoptr v44 : *mut i32; - store v45, v42; - v47 = const.i32 2 : i32; - v48 = shl.wrapping v42, v47 : i32; - v49 = add.wrapping v0, v48 : i32; - v50 = cast v46 : u32; - v51 = add.checked v50, 412 : u32; - v52 = inttoptr v51 : *mut i32; - v53 = load v52 : i32; - v54 = const.i32 1 : i32; - v55 = shl.wrapping v54, v42 : i32; - v56 = band v53, v55 : i32; - v57 = neq v56, 0 : i1; - condbr v57, block6, block7; - -block3: - v11 = const.i32 31 : i32; - v12 = const.i32 16777215 : i32; - v13 = cast v2 : u32; - v14 = cast v12 : u32; - v15 = gt v13, v14 : i1; - v16 = cast v15 : i32; - v17 = neq v16, 0 : i1; - condbr v17, block2(v11), block4; - -block4: - v18 = const.i32 6 : i32; - v19 = const.i32 8 : i32; - v20 = cast v2 : u32; - v21 = cast v19 : u32; - v22 = shr.wrapping v20, v21 : u32; - v23 = cast v22 : i32; - v24 = popcnt v23 : i32; - v25 = sub.wrapping v18, v24 : i32; - v26 = cast v2 : u32; - v27 = cast v25 : u32; - v28 = shr.wrapping v26, v27 : u32; - v29 = cast v28 : i32; - v30 = const.i32 1 : i32; - v31 = band v29, v30 : i32; - v32 = const.i32 1 : i32; - v33 = shl.wrapping v24, v32 : i32; - v34 = sub.wrapping v31, v33 : i32; - v35 = const.i32 62 : i32; - v36 = add.wrapping v34, v35 : i32; - br block2(v36); - -block5(v141: i32): - v142 = cast v141 : u32; - v143 = add.checked v142, 12 : u32; - v144 = inttoptr v143 : *mut i32; - store v144, v141; - v145 = cast v141 : u32; - v146 = add.checked v145, 8 : u32; - v147 = inttoptr v146 : *mut i32; - store v147, v141; - br block1; - -block6: - v68 = const.i32 0 : i32; - v69 = const.i32 25 : i32; - v70 = const.i32 1 : i32; - v71 = cast v42 : u32; - v72 = cast v70 : u32; - v73 = shr.wrapping v71, v72 : u32; - v74 = cast v73 : i32; - v75 = sub.wrapping v69, v74 : i32; - v76 = const.i32 31 : i32; - v77 = band v75, v76 : i32; - v78 = const.i32 31 : i32; - v79 = eq v42, v78 : i1; - v80 = cast v79 : i32; - v81 = neq v80, 0 : i1; - v82 = select v81, v68, v77 : i32; - v83 = shl.wrapping v2, v82 : i32; - v84 = cast v49 : u32; - v85 = inttoptr v84 : *mut i32; - v86 = load v85 : i32; - br block8(v86, v83); - -block7: - v58 = bor v53, v55 : i32; - v59 = cast v46 : u32; - v60 = add.checked v59, 412 : u32; - v61 = inttoptr v60 : *mut i32; - store v61, v58; - v62 = cast v37 : u32; - v63 = add.checked v62, 24 : u32; - v64 = inttoptr v63 : *mut i32; - store v64, v49; - v65 = cast v49 : u32; - v66 = inttoptr v65 : *mut i32; - store v66, v37; - br block5(v37); - -block8(v87: i32, v119: i32): - v88 = cast v87 : u32; - v89 = add.checked v88, 4 : u32; - v90 = inttoptr v89 : *mut i32; - v91 = load v90 : i32; - v92 = const.i32 -8 : i32; - v93 = band v91, v92 : i32; - v95 = neq v93, v94 : i1; - v96 = cast v95 : i32; - v97 = neq v96, 0 : i1; - condbr v97, block10, block11; - -block9: - v136 = cast v131 : u32; - v137 = inttoptr v136 : *mut i32; - store v137, v102; - v138 = cast v102 : u32; - v139 = add.checked v138, 24 : u32; - v140 = inttoptr v139 : *mut i32; - store v140, v87; - br block5(v102); - -block10: - v120 = const.i32 29 : i32; - v121 = cast v119 : u32; - v122 = cast v120 : u32; - v123 = shr.wrapping v121, v122 : u32; - v124 = cast v123 : i32; - v125 = const.i32 1 : i32; - v126 = shl.wrapping v119, v125 : i32; - v127 = const.i32 4 : i32; - v128 = band v124, v127 : i32; - v129 = add.wrapping v87, v128 : i32; - v130 = const.i32 16 : i32; - v131 = add.wrapping v129, v130 : i32; - v132 = cast v131 : u32; - v133 = inttoptr v132 : *mut i32; - v134 = load v133 : i32; - v135 = neq v134, 0 : i1; - condbr v135, block8(v134, v126), block12; - -block11: - v98 = cast v87 : u32; - v99 = add.checked v98, 8 : u32; - v100 = inttoptr v99 : *mut i32; - v101 = load v100 : i32; - v103 = cast v101 : u32; - v104 = add.checked v103, 12 : u32; - v105 = inttoptr v104 : *mut i32; - store v105, v102; - v106 = cast v87 : u32; - v107 = add.checked v106, 8 : u32; - v108 = inttoptr v107 : *mut i32; - store v108, v102; - v109 = const.i32 0 : i32; - v110 = cast v102 : u32; - v111 = add.checked v110, 24 : u32; - v112 = inttoptr v111 : *mut i32; - store v112, v109; - v113 = cast v102 : u32; - v114 = add.checked v113, 12 : u32; - v115 = inttoptr v114 : *mut i32; - store v115, v87; - v116 = cast v102 : u32; - v117 = add.checked v116, 8 : u32; - v118 = inttoptr v117 : *mut i32; - store v118, v101; - ret; - -block12: - br block9; -} - -pub fn dlmalloc::dlmalloc::Dlmalloc::free(i32, i32) { -block0(v0: i32, v1: i32): - v2 = const.i32 0 : i32; - v3 = const.i32 -8 : i32; - v4 = add.wrapping v1, v3 : i32; - v5 = const.i32 -4 : i32; - v6 = add.wrapping v1, v5 : i32; - v7 = cast v6 : u32; - v8 = inttoptr v7 : *mut i32; - v9 = load v8 : i32; - v10 = const.i32 -8 : i32; - v11 = band v9, v10 : i32; - v12 = add.wrapping v4, v11 : i32; - v13 = const.i32 1 : i32; - v14 = band v9, v13 : i32; - v15 = neq v14, 0 : i1; - condbr v15, block7(v11, v4), block8; - -block1: - ret; - -block2: - v403 = cast v104 : u32; - v404 = add.checked v403, 416 : u32; - v405 = inttoptr v404 : *mut i32; - v406 = load v405 : i32; - v407 = add.wrapping v406, v121 : i32; - v408 = const.i32 1 : i32; - v409 = bor v407, v408 : i32; - v410 = cast v162 : u32; - v411 = add.checked v410, 4 : u32; - v412 = inttoptr v411 : *mut i32; - store v412, v409; - v413 = cast v104 : u32; - v414 = add.checked v413, 424 : u32; - v415 = inttoptr v414 : *mut i32; - store v415, v162; - v416 = cast v104 : u32; - v417 = add.checked v416, 416 : u32; - v418 = inttoptr v417 : *mut i32; - store v418, v407; - v419 = add.wrapping v162, v407 : i32; - v420 = cast v419 : u32; - v421 = inttoptr v420 : *mut i32; - store v421, v407; - br block1; - -block3(v391: i32): - v392 = const.i32 4095 : i32; - v393 = const.i32 4095 : i32; - v394 = cast v391 : u32; - v395 = cast v393 : u32; - v396 = gt v394, v395 : i1; - v397 = cast v396 : i32; - v398 = neq v397, 0 : i1; - v399 = select v398, v391, v392 : i32; - v400 = cast v203 : u32; - v401 = add.checked v400, 448 : u32; - v402 = inttoptr v401 : *mut i32; - store v402, v399; - ret; - -block4: - v379 = const.i32 0 : i32; - br block52(v379, v219); - -block5: - v338 = const.i32 -8 : i32; - v339 = band v196, v338 : i32; - v340 = add.wrapping v203, v339 : i32; - v341 = const.i32 144 : i32; - v342 = add.wrapping v340, v341 : i32; - v343 = cast v203 : u32; - v344 = add.checked v343, 408 : u32; - v345 = inttoptr v344 : *mut i32; - v346 = load v345 : i32; - v347 = const.i32 1 : i32; - v348 = const.i32 3 : i32; - v349 = cast v196 : u32; - v350 = cast v348 : u32; - v351 = shr.wrapping v349, v350 : u32; - v352 = cast v351 : i32; - v353 = shl.wrapping v347, v352 : i32; - v354 = band v346, v353 : i32; - v355 = neq v354, 0 : i1; - condbr v355, block50, block51; - -block6: - ret; - -block7(v121: i32, v162: i32): - v97 = cast v12 : u32; - v98 = add.checked v97, 4 : u32; - v99 = inttoptr v98 : *mut i32; - v100 = load v99 : i32; - v101 = const.i32 2 : i32; - v102 = band v100, v101 : i32; - v103 = neq v102, 0 : i1; - condbr v103, block19, block20; - -block8: - v16 = const.i32 3 : i32; - v17 = band v9, v16 : i32; - v18 = eq v17, 0 : i1; - v19 = cast v18 : i32; - v20 = neq v19, 0 : i1; - condbr v20, block6, block9; - -block9: - v21 = cast v4 : u32; - v22 = inttoptr v21 : *mut i32; - v23 = load v22 : i32; - v24 = add.wrapping v23, v11 : i32; - v25 = sub.wrapping v4, v23 : i32; - v26 = cast v0 : u32; - v27 = add.checked v26, 424 : u32; - v28 = inttoptr v27 : *mut i32; - v29 = load v28 : i32; - v30 = neq v25, v29 : i1; - v31 = cast v30 : i32; - v32 = neq v31, 0 : i1; - condbr v32, block10, block11; - -block10: - v58 = const.i32 256 : i32; - v59 = cast v23 : u32; - v60 = cast v58 : u32; - v61 = lt v59, v60 : i1; - v62 = cast v61 : i32; - v63 = neq v62, 0 : i1; - condbr v63, block13, block14; - -block11: - v33 = cast v12 : u32; - v34 = add.checked v33, 4 : u32; - v35 = inttoptr v34 : *mut i32; - v36 = load v35 : i32; - v37 = const.i32 3 : i32; - v38 = band v36, v37 : i32; - v39 = const.i32 3 : i32; - v40 = neq v38, v39 : i1; - v41 = cast v40 : i32; - v42 = neq v41, 0 : i1; - condbr v42, block7(v24, v25), block12; - -block12: - v43 = const.i32 -2 : i32; - v44 = band v36, v43 : i32; - v45 = cast v12 : u32; - v46 = add.checked v45, 4 : u32; - v47 = inttoptr v46 : *mut i32; - store v47, v44; - v48 = const.i32 1 : i32; - v49 = bor v24, v48 : i32; - v50 = cast v25 : u32; - v51 = add.checked v50, 4 : u32; - v52 = inttoptr v51 : *mut i32; - store v52, v49; - v53 = cast v0 : u32; - v54 = add.checked v53, 416 : u32; - v55 = inttoptr v54 : *mut i32; - store v55, v24; - v56 = cast v12 : u32; - v57 = inttoptr v56 : *mut i32; - store v57, v24; - ret; - -block13: - v64 = cast v25 : u32; - v65 = add.checked v64, 12 : u32; - v66 = inttoptr v65 : *mut i32; - v67 = load v66 : i32; - v68 = cast v25 : u32; - v69 = add.checked v68, 8 : u32; - v70 = inttoptr v69 : *mut i32; - v71 = load v70 : i32; - v72 = eq v67, v71 : i1; - v73 = cast v72 : i32; - v74 = neq v73, 0 : i1; - condbr v74, block15, block16; - -block14: - call noname::dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(v0, v25); - br block7(v24, v25); - -block15: - v81 = cast v0 : u32; - v82 = add.checked v81, 408 : u32; - v83 = inttoptr v82 : *mut i32; - v84 = load v83 : i32; - v85 = const.i32 -2 : i32; - v86 = const.i32 3 : i32; - v87 = cast v23 : u32; - v88 = cast v86 : u32; - v89 = shr.wrapping v87, v88 : u32; - v90 = cast v89 : i32; - v91 = rotl v85, v90 : i32; - v92 = band v84, v91 : i32; - v93 = cast v0 : u32; - v94 = add.checked v93, 408 : u32; - v95 = inttoptr v94 : *mut i32; - store v95, v92; - br block7(v24, v25); - -block16: - v75 = cast v71 : u32; - v76 = add.checked v75, 12 : u32; - v77 = inttoptr v76 : *mut i32; - store v77, v67; - v78 = cast v67 : u32; - v79 = add.checked v78, 8 : u32; - v80 = inttoptr v79 : *mut i32; - store v80, v71; - br block7(v24, v25); - -block17: - v222 = cast v104 : u32; - v223 = add.checked v222, 428 : u32; - v224 = inttoptr v223 : *mut i32; - store v224, v162; - v225 = cast v104 : u32; - v226 = add.checked v225, 420 : u32; - v227 = inttoptr v226 : *mut i32; - v228 = load v227 : i32; - v229 = add.wrapping v228, v121 : i32; - v230 = cast v104 : u32; - v231 = add.checked v230, 420 : u32; - v232 = inttoptr v231 : *mut i32; - store v232, v229; - v233 = const.i32 1 : i32; - v234 = bor v229, v233 : i32; - v235 = cast v162 : u32; - v236 = add.checked v235, 4 : u32; - v237 = inttoptr v236 : *mut i32; - store v237, v234; - v238 = cast v104 : u32; - v239 = add.checked v238, 424 : u32; - v240 = inttoptr v239 : *mut i32; - v241 = load v240 : i32; - v242 = neq v162, v241 : i1; - v243 = cast v242 : i32; - v244 = neq v243, 0 : i1; - condbr v244, block32, block33; - -block18(v196: i32, v203: i32, v204: i32): - v197 = const.i32 256 : i32; - v198 = cast v196 : u32; - v199 = cast v197 : u32; - v200 = lt v198, v199 : i1; - v201 = cast v200 : i32; - v202 = neq v201, 0 : i1; - condbr v202, block5, block29; - -block19: - v183 = const.i32 -2 : i32; - v184 = band v100, v183 : i32; - v185 = cast v96 : u32; - v186 = add.checked v185, 4 : u32; - v187 = inttoptr v186 : *mut i32; - store v187, v184; - v188 = const.i32 1 : i32; - v189 = bor v121, v188 : i32; - v190 = cast v162 : u32; - v191 = add.checked v190, 4 : u32; - v192 = inttoptr v191 : *mut i32; - store v192, v189; - v193 = add.wrapping v162, v121 : i32; - v194 = cast v193 : u32; - v195 = inttoptr v194 : *mut i32; - store v195, v121; - br block18(v121, v104, v162); - -block20: - v105 = cast v0 : u32; - v106 = add.checked v105, 428 : u32; - v107 = inttoptr v106 : *mut i32; - v108 = load v107 : i32; - v109 = eq v96, v108 : i1; - v110 = cast v109 : i32; - v111 = neq v110, 0 : i1; - condbr v111, block17, block21; - -block21: - v112 = cast v104 : u32; - v113 = add.checked v112, 424 : u32; - v114 = inttoptr v113 : *mut i32; - v115 = load v114 : i32; - v116 = eq v96, v115 : i1; - v117 = cast v116 : i32; - v118 = neq v117, 0 : i1; - condbr v118, block2, block22; - -block22: - v119 = const.i32 -8 : i32; - v120 = band v100, v119 : i32; - v122 = add.wrapping v120, v121 : i32; - v123 = const.i32 256 : i32; - v124 = cast v120 : u32; - v125 = cast v123 : u32; - v126 = lt v124, v125 : i1; - v127 = cast v126 : i32; - v128 = neq v127, 0 : i1; - condbr v128, block24, block25; - -block23: - v164 = const.i32 1 : i32; - v165 = bor v122, v164 : i32; - v166 = cast v162 : u32; - v167 = add.checked v166, 4 : u32; - v168 = inttoptr v167 : *mut i32; - store v168, v165; - v169 = add.wrapping v161, v163 : i32; - v170 = cast v169 : u32; - v171 = inttoptr v170 : *mut i32; - store v171, v163; - v173 = cast v104 : u32; - v174 = add.checked v173, 424 : u32; - v175 = inttoptr v174 : *mut i32; - v176 = load v175 : i32; - v177 = neq v161, v176 : i1; - v178 = cast v177 : i32; - v179 = neq v178, 0 : i1; - condbr v179, block18(v163, v172, v161), block28; - -block24: - v129 = cast v96 : u32; - v130 = add.checked v129, 12 : u32; - v131 = inttoptr v130 : *mut i32; - v132 = load v131 : i32; - v133 = cast v96 : u32; - v134 = add.checked v133, 8 : u32; - v135 = inttoptr v134 : *mut i32; - v136 = load v135 : i32; - v137 = eq v132, v136 : i1; - v138 = cast v137 : i32; - v139 = neq v138, 0 : i1; - condbr v139, block26, block27; - -block25: - call noname::dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(v104, v96); - br block23; - -block26: - v146 = cast v104 : u32; - v147 = add.checked v146, 408 : u32; - v148 = inttoptr v147 : *mut i32; - v149 = load v148 : i32; - v150 = const.i32 -2 : i32; - v151 = const.i32 3 : i32; - v152 = cast v100 : u32; - v153 = cast v151 : u32; - v154 = shr.wrapping v152, v153 : u32; - v155 = cast v154 : i32; - v156 = rotl v150, v155 : i32; - v157 = band v149, v156 : i32; - v158 = cast v104 : u32; - v159 = add.checked v158, 408 : u32; - v160 = inttoptr v159 : *mut i32; - store v160, v157; - br block23; - -block27: - v140 = cast v136 : u32; - v141 = add.checked v140, 12 : u32; - v142 = inttoptr v141 : *mut i32; - store v142, v132; - v143 = cast v132 : u32; - v144 = add.checked v143, 8 : u32; - v145 = inttoptr v144 : *mut i32; - store v145, v136; - br block23; - -block28: - v180 = cast v172 : u32; - v181 = add.checked v180, 416 : u32; - v182 = inttoptr v181 : *mut i32; - store v182, v163; - ret; - -block29: - call noname::dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk(v203, v204, v196); - v205 = cast v203 : u32; - v206 = add.checked v205, 448 : u32; - v207 = inttoptr v206 : *mut i32; - v208 = load v207 : i32; - v209 = const.i32 -1 : i32; - v210 = add.wrapping v208, v209 : i32; - v211 = cast v203 : u32; - v212 = add.checked v211, 448 : u32; - v213 = inttoptr v212 : *mut i32; - store v213, v210; - v214 = neq v210, 0 : i1; - condbr v214, block6, block30; - -block30: - v215 = const.i32 136 : i32; - v216 = add.wrapping v203, v215 : i32; - v217 = cast v216 : u32; - v218 = inttoptr v217 : *mut i32; - v219 = load v218 : i32; - v220 = neq v219, 0 : i1; - condbr v220, block4, block31; - -block31: - v221 = const.i32 0 : i32; - br block3(v221); - -block32: - v255 = cast v104 : u32; - v256 = add.checked v255, 440 : u32; - v257 = inttoptr v256 : *mut i32; - v258 = load v257 : i32; - v259 = cast v229 : u32; - v260 = cast v258 : u32; - v261 = lte v259, v260 : i1; - v262 = cast v261 : i32; - v263 = neq v262, 0 : i1; - condbr v263, block6, block34; - -block33: - v245 = const.i32 0 : i32; - v246 = cast v104 : u32; - v247 = add.checked v246, 416 : u32; - v248 = inttoptr v247 : *mut i32; - store v248, v245; - v249 = const.i32 0 : i32; - v250 = cast v104 : u32; - v251 = add.checked v250, 424 : u32; - v252 = inttoptr v251 : *mut i32; - store v252, v249; - br block32; - -block34: - v264 = const.i32 41 : i32; - v265 = cast v253 : u32; - v266 = cast v264 : u32; - v267 = lt v265, v266 : i1; - v268 = cast v267 : i32; - v269 = neq v268, 0 : i1; - condbr v269, block35(v254), block36; - -block35(v300: i32): - v303 = const.i32 136 : i32; - v304 = add.wrapping v300, v303 : i32; - v305 = cast v304 : u32; - v306 = inttoptr v305 : *mut i32; - v307 = load v306 : i32; - v308 = neq v307, 0 : i1; - condbr v308, block44, block45; - -block36: - v270 = const.i32 128 : i32; - v271 = add.wrapping v254, v270 : i32; - br block37(v271); - -block37(v272: i32): - v273 = cast v272 : u32; - v274 = inttoptr v273 : *mut i32; - v275 = load v274 : i32; - v277 = cast v275 : u32; - v278 = cast v276 : u32; - v279 = gt v277, v278 : i1; - v280 = cast v279 : i32; - v281 = neq v280, 0 : i1; - condbr v281, block39, block40; - -block38: - br block35(v302); - -block39: - v293 = cast v272 : u32; - v294 = add.checked v293, 8 : u32; - v295 = inttoptr v294 : *mut i32; - v296 = load v295 : i32; - v297 = neq v296, 0 : i1; - condbr v297, block37(v296), block42; - -block40: - v282 = cast v272 : u32; - v283 = add.checked v282, 4 : u32; - v284 = inttoptr v283 : *mut i32; - v285 = load v284 : i32; - v286 = add.wrapping v275, v285 : i32; - v287 = cast v286 : u32; - v288 = cast v276 : u32; - v289 = gt v287, v288 : i1; - v290 = cast v289 : i32; - v291 = neq v290, 0 : i1; - condbr v291, block35(v254), block41; - -block41: - br block39; - -block42: - br block38; - -block43(v326: i32): - v322 = const.i32 -1 : i32; - v323 = cast v300 : u32; - v324 = add.checked v323, 440 : u32; - v325 = inttoptr v324 : *mut i32; - store v325, v322; - v327 = const.i32 4095 : i32; - v328 = const.i32 4095 : i32; - v329 = cast v326 : u32; - v330 = cast v328 : u32; - v331 = gt v329, v330 : i1; - v332 = cast v331 : i32; - v333 = neq v332, 0 : i1; - v334 = select v333, v326, v327 : i32; - v335 = cast v320 : u32; - v336 = add.checked v335, 448 : u32; - v337 = inttoptr v336 : *mut i32; - store v337, v334; - br block6; - -block44: - v310 = const.i32 0 : i32; - br block46(v310, v307); - -block45: - v309 = const.i32 0 : i32; - br block43(v309); - -block46(v311: i32, v314: i32): - v312 = const.i32 1 : i32; - v313 = add.wrapping v311, v312 : i32; - v315 = cast v314 : u32; - v316 = add.checked v315, 8 : u32; - v317 = inttoptr v316 : *mut i32; - v318 = load v317 : i32; - v319 = neq v318, 0 : i1; - condbr v319, block46(v313, v318), block48; - -block47: - br block43(v313); - -block48: - br block47; - -block49(v369: i32): - v366 = cast v342 : u32; - v367 = add.checked v366, 8 : u32; - v368 = inttoptr v367 : *mut i32; - store v368, v204; - v370 = cast v369 : u32; - v371 = add.checked v370, 12 : u32; - v372 = inttoptr v371 : *mut i32; - store v372, v365; - v373 = cast v365 : u32; - v374 = add.checked v373, 12 : u32; - v375 = inttoptr v374 : *mut i32; - store v375, v364; - v376 = cast v365 : u32; - v377 = add.checked v376, 8 : u32; - v378 = inttoptr v377 : *mut i32; - store v378, v369; - ret; - -block50: - v360 = cast v342 : u32; - v361 = add.checked v360, 8 : u32; - v362 = inttoptr v361 : *mut i32; - v363 = load v362 : i32; - br block49(v363); - -block51: - v356 = bor v346, v353 : i32; - v357 = cast v203 : u32; - v358 = add.checked v357, 408 : u32; - v359 = inttoptr v358 : *mut i32; - store v359, v356; - br block49(v342); - -block52(v380: i32, v383: i32): - v381 = const.i32 1 : i32; - v382 = add.wrapping v380, v381 : i32; - v384 = cast v383 : u32; - v385 = add.checked v384, 8 : u32; - v386 = inttoptr v385 : *mut i32; - v387 = load v386 : i32; - v388 = neq v387, 0 : i1; - condbr v388, block52(v382, v387), block54; - -block53: - br block3(v382); - -block54: - br block53; -} - -pub fn dlmalloc::dlmalloc::Dlmalloc::malloc(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 0 : i32; - v4 = const.i64 0 : i64; - v5 = const.i32 245 : i32; - v6 = cast v1 : u32; - v7 = cast v5 : u32; - v8 = lt v6, v7 : i1; - v9 = cast v8 : i32; - v10 = neq v9, 0 : i1; - condbr v10, block7, block8; - -block1(v2: i32): - ret v2; - -block2(v1563: i32): - br block1(v1563); - -block3(v738: i32, v743: i32): - v739 = cast v738 : u32; - v740 = add.checked v739, 416 : u32; - v741 = inttoptr v740 : *mut i32; - v742 = load v741 : i32; - v744 = cast v742 : u32; - v745 = cast v743 : u32; - v746 = gte v744, v745 : i1; - v747 = cast v746 : i32; - v748 = neq v747, 0 : i1; - condbr v748, block82, block83; - -block4(v621: i32, v625: i32, v634: i32, v640: i32): - v622 = eq v621, 0 : i1; - v623 = cast v622 : i32; - v624 = neq v623, 0 : i1; - condbr v624, block3(v625, v634), block63; - -block5(v611: i32, v612: i32, v614: i32, v618: i32, v629: i32): - br block58(v611, v612, v618, v629); - -block6(v529: i32, v531: i32, v538: i32, v545: i32, v553: i32, v616: i32, v620: i32): - v534 = bor v529, v531 : i32; - v535 = neq v534, 0 : i1; - condbr v535, block54(v529, v531), block55; - -block7: - v150 = cast v0 : u32; - v151 = add.checked v150, 408 : u32; - v152 = inttoptr v151 : *mut i32; - v153 = load v152 : i32; - v154 = const.i32 16 : i32; - v155 = const.i32 11 : i32; - v156 = add.wrapping v1, v155 : i32; - v157 = const.i32 -8 : i32; - v158 = band v156, v157 : i32; - v159 = const.i32 11 : i32; - v160 = cast v1 : u32; - v161 = cast v159 : u32; - v162 = lt v160, v161 : i1; - v163 = cast v162 : i32; - v164 = neq v163, 0 : i1; - v165 = select v164, v154, v158 : i32; - v166 = const.i32 3 : i32; - v167 = cast v165 : u32; - v168 = cast v166 : u32; - v169 = shr.wrapping v167, v168 : u32; - v170 = cast v169 : i32; - v171 = cast v153 : u32; - v172 = cast v170 : u32; - v173 = shr.wrapping v171, v172 : u32; - v174 = cast v173 : i32; - v175 = const.i32 3 : i32; - v176 = band v174, v175 : i32; - v177 = eq v176, 0 : i1; - v178 = cast v177 : i32; - v179 = neq v178, 0 : i1; - condbr v179, block23, block24; - -block8: - v11 = const.i32 0 : i32; - v12 = const.i32 -65587 : i32; - v13 = cast v1 : u32; - v14 = cast v12 : u32; - v15 = gte v13, v14 : i1; - v16 = cast v15 : i32; - v17 = neq v16, 0 : i1; - condbr v17, block2(v11), block9; - -block9: - v18 = const.i32 11 : i32; - v19 = add.wrapping v1, v18 : i32; - v20 = const.i32 -8 : i32; - v21 = band v19, v20 : i32; - v22 = cast v0 : u32; - v23 = add.checked v22, 412 : u32; - v24 = inttoptr v23 : *mut i32; - v25 = load v24 : i32; - v26 = eq v25, 0 : i1; - v27 = cast v26 : i32; - v28 = neq v27, 0 : i1; - condbr v28, block3(v0, v21), block10; - -block10: - v29 = const.i32 0 : i32; - v30 = const.i32 0 : i32; - v31 = const.i32 256 : i32; - v32 = cast v21 : u32; - v33 = cast v31 : u32; - v34 = lt v32, v33 : i1; - v35 = cast v34 : i32; - v36 = neq v35, 0 : i1; - condbr v36, block11(v30), block12; - -block11(v67: i32): - v63 = const.i32 0 : i32; - v65 = sub.wrapping v63, v21 : i32; - v68 = const.i32 2 : i32; - v69 = shl.wrapping v67, v68 : i32; - v70 = add.wrapping v0, v69 : i32; - v71 = cast v70 : u32; - v72 = inttoptr v71 : *mut i32; - v73 = load v72 : i32; - v74 = neq v73, 0 : i1; - condbr v74, block14, block15; - -block12: - v37 = const.i32 31 : i32; - v38 = const.i32 16777215 : i32; - v39 = cast v21 : u32; - v40 = cast v38 : u32; - v41 = gt v39, v40 : i1; - v42 = cast v41 : i32; - v43 = neq v42, 0 : i1; - condbr v43, block11(v37), block13; - -block13: - v44 = const.i32 6 : i32; - v45 = const.i32 8 : i32; - v46 = cast v19 : u32; - v47 = cast v45 : u32; - v48 = shr.wrapping v46, v47 : u32; - v49 = cast v48 : i32; - v50 = popcnt v49 : i32; - v51 = sub.wrapping v44, v50 : i32; - v52 = cast v21 : u32; - v53 = cast v51 : u32; - v54 = shr.wrapping v52, v53 : u32; - v55 = cast v54 : i32; - v56 = const.i32 1 : i32; - v57 = band v55, v56 : i32; - v58 = const.i32 1 : i32; - v59 = shl.wrapping v50, v58 : i32; - v60 = sub.wrapping v57, v59 : i32; - v61 = const.i32 62 : i32; - v62 = add.wrapping v60, v61 : i32; - br block11(v62); - -block14: - v76 = const.i32 0 : i32; - v77 = const.i32 0 : i32; - v78 = const.i32 25 : i32; - v79 = const.i32 1 : i32; - v80 = cast v67 : u32; - v81 = cast v79 : u32; - v82 = shr.wrapping v80, v81 : u32; - v83 = cast v82 : i32; - v84 = sub.wrapping v78, v83 : i32; - v85 = const.i32 31 : i32; - v86 = eq v67, v85 : i1; - v87 = cast v86 : i32; - v88 = neq v87, 0 : i1; - v89 = select v88, v77, v84 : i32; - v90 = shl.wrapping v64, v89 : i32; - v91 = const.i32 0 : i32; - br block16(v73, v65, v76, v90, v91, v67, v546, v66); - -block15: - v75 = const.i32 0 : i32; - br block6(v29, v75, v67, v25, v66, v64, v65); - -block16(v92: i32, v106: i32, v121: i32, v123: i32, v533: i32, v540: i32, v548: i32, v555: i32): - v93 = cast v92 : u32; - v94 = add.checked v93, 4 : u32; - v95 = inttoptr v94 : *mut i32; - v96 = load v95 : i32; - v97 = const.i32 -8 : i32; - v98 = band v96, v97 : i32; - v100 = cast v98 : u32; - v101 = cast v99 : u32; - v102 = lt v100, v101 : i1; - v103 = cast v102 : i32; - v104 = neq v103, 0 : i1; - condbr v104, block18(v106, v533), block19; - -block17: - -block18(v149: i32, v532: i32): - v115 = const.i32 20 : i32; - v116 = add.wrapping v92, v115 : i32; - v117 = cast v116 : u32; - v118 = inttoptr v117 : *mut i32; - v119 = load v118 : i32; - v124 = const.i32 29 : i32; - v125 = cast v123 : u32; - v126 = cast v124 : u32; - v127 = shr.wrapping v125, v126 : u32; - v128 = cast v127 : i32; - v129 = const.i32 4 : i32; - v130 = band v128, v129 : i32; - v131 = add.wrapping v114, v130 : i32; - v132 = const.i32 16 : i32; - v133 = add.wrapping v131, v132 : i32; - v134 = cast v133 : u32; - v135 = inttoptr v134 : *mut i32; - v136 = load v135 : i32; - v137 = neq v119, v136 : i1; - v138 = cast v137 : i32; - v139 = neq v138, 0 : i1; - v140 = select v139, v119, v121 : i32; - v141 = neq v119, 0 : i1; - v142 = select v141, v140, v120 : i32; - v143 = const.i32 1 : i32; - v144 = shl.wrapping v122, v143 : i32; - v145 = eq v136, 0 : i1; - v146 = cast v145 : i32; - v147 = neq v146, 0 : i1; - condbr v147, block6(v142, v532, v540, v548, v555, v148, v149), block22; - -block19: - v105 = sub.wrapping v98, v99 : i32; - v107 = cast v105 : u32; - v108 = cast v106 : u32; - v109 = gte v107, v108 : i1; - v110 = cast v109 : i32; - v111 = neq v110, 0 : i1; - condbr v111, block18(v106, v533), block20; - -block20: - v112 = neq v105, 0 : i1; - condbr v112, block18(v105, v92), block21; - -block21: - v113 = const.i32 0 : i32; - br block5(v92, v92, v99, v113, v555); - -block22: - br block16(v136, v149, v142, v144, v532, v539, v547, v554); - -block23: - v235 = cast v0 : u32; - v236 = add.checked v235, 416 : u32; - v237 = inttoptr v236 : *mut i32; - v238 = load v237 : i32; - v239 = cast v165 : u32; - v240 = cast v238 : u32; - v241 = lte v239, v240 : i1; - v242 = cast v241 : i32; - v243 = neq v242, 0 : i1; - condbr v243, block3(v0, v165), block28; - -block24: - v180 = const.i32 -1 : i32; - v181 = bxor v174, v180 : i32; - v182 = const.i32 1 : i32; - v183 = band v181, v182 : i32; - v184 = add.wrapping v183, v170 : i32; - v185 = const.i32 3 : i32; - v186 = shl.wrapping v184, v185 : i32; - v187 = add.wrapping v0, v186 : i32; - v188 = const.i32 152 : i32; - v189 = add.wrapping v187, v188 : i32; - v190 = cast v189 : u32; - v191 = inttoptr v190 : *mut i32; - v192 = load v191 : i32; - v193 = cast v192 : u32; - v194 = add.checked v193, 8 : u32; - v195 = inttoptr v194 : *mut i32; - v196 = load v195 : i32; - v197 = const.i32 144 : i32; - v198 = add.wrapping v187, v197 : i32; - v199 = eq v196, v198 : i1; - v200 = cast v199 : i32; - v201 = neq v200, 0 : i1; - condbr v201, block26, block27; - -block25: - v216 = const.i32 3 : i32; - v217 = shl.wrapping v184, v216 : i32; - v218 = const.i32 3 : i32; - v219 = bor v217, v218 : i32; - v220 = cast v192 : u32; - v221 = add.checked v220, 4 : u32; - v222 = inttoptr v221 : *mut i32; - store v222, v219; - v223 = add.wrapping v214, v217 : i32; - v224 = cast v223 : u32; - v225 = add.checked v224, 4 : u32; - v226 = inttoptr v225 : *mut i32; - v227 = load v226 : i32; - v228 = const.i32 1 : i32; - v229 = bor v227, v228 : i32; - v230 = cast v223 : u32; - v231 = add.checked v230, 4 : u32; - v232 = inttoptr v231 : *mut i32; - store v232, v229; - v233 = const.i32 8 : i32; - v234 = add.wrapping v214, v233 : i32; - ret v234; - -block26: - v208 = const.i32 -2 : i32; - v209 = rotl v208, v184 : i32; - v210 = band v153, v209 : i32; - v211 = cast v0 : u32; - v212 = add.checked v211, 408 : u32; - v213 = inttoptr v212 : *mut i32; - store v213, v210; - br block25; - -block27: - v202 = cast v196 : u32; - v203 = add.checked v202, 12 : u32; - v204 = inttoptr v203 : *mut i32; - store v204, v198; - v205 = cast v198 : u32; - v206 = add.checked v205, 8 : u32; - v207 = inttoptr v206 : *mut i32; - store v207, v196; - br block25; - -block28: - v244 = neq v174, 0 : i1; - condbr v244, block31, block32; - -block29: - v527 = const.i32 8 : i32; - v528 = add.wrapping v279, v527 : i32; - ret v528; - -block30: - v516 = cast v278 : u32; - v517 = add.checked v516, 424 : u32; - v518 = inttoptr v517 : *mut i32; - store v518, v293; - v521 = cast v512 : u32; - v522 = add.checked v521, 416 : u32; - v523 = inttoptr v522 : *mut i32; - store v523, v280; - br block29; - -block31: - v388 = const.i32 144 : i32; - v389 = add.wrapping v0, v388 : i32; - v390 = shl.wrapping v174, v170 : i32; - v391 = const.i32 2 : i32; - v392 = shl.wrapping v391, v170 : i32; - v393 = const.i32 0 : i32; - v394 = sub.wrapping v393, v392 : i32; - v395 = bor v392, v394 : i32; - v396 = band v390, v395 : i32; - v397 = popcnt v396 : i32; - v398 = const.i32 3 : i32; - v399 = shl.wrapping v397, v398 : i32; - v400 = add.wrapping v389, v399 : i32; - v401 = cast v400 : u32; - v402 = add.checked v401, 8 : u32; - v403 = inttoptr v402 : *mut i32; - v404 = load v403 : i32; - v405 = cast v404 : u32; - v406 = add.checked v405, 8 : u32; - v407 = inttoptr v406 : *mut i32; - v408 = load v407 : i32; - v409 = eq v408, v400 : i1; - v410 = cast v409 : i32; - v411 = neq v410, 0 : i1; - condbr v411, block47, block48; - -block32: - v245 = cast v0 : u32; - v246 = add.checked v245, 412 : u32; - v247 = inttoptr v246 : *mut i32; - v248 = load v247 : i32; - v249 = eq v248, 0 : i1; - v250 = cast v249 : i32; - v251 = neq v250, 0 : i1; - condbr v251, block3(v0, v165), block33; - -block33: - v252 = popcnt v248 : i32; - v253 = const.i32 2 : i32; - v254 = shl.wrapping v252, v253 : i32; - v255 = add.wrapping v0, v254 : i32; - v256 = cast v255 : u32; - v257 = inttoptr v256 : *mut i32; - v258 = load v257 : i32; - v259 = cast v258 : u32; - v260 = add.checked v259, 4 : u32; - v261 = inttoptr v260 : *mut i32; - v262 = load v261 : i32; - v263 = const.i32 -8 : i32; - v264 = band v262, v263 : i32; - v265 = sub.wrapping v264, v165 : i32; - br block36(v258, v258, v265, v165); - -block34: - v372 = add.wrapping v280, v287 : i32; - v373 = const.i32 3 : i32; - v374 = bor v372, v373 : i32; - v375 = cast v279 : u32; - v376 = add.checked v375, 4 : u32; - v377 = inttoptr v376 : *mut i32; - store v377, v374; - v378 = add.wrapping v279, v372 : i32; - v379 = cast v378 : u32; - v380 = add.checked v379, 4 : u32; - v381 = inttoptr v380 : *mut i32; - v382 = load v381 : i32; - v383 = const.i32 1 : i32; - v384 = bor v382, v383 : i32; - v385 = cast v378 : u32; - v386 = add.checked v385, 4 : u32; - v387 = inttoptr v386 : *mut i32; - store v387, v384; - br block29; - -block35: - v327 = const.i32 -8 : i32; - v328 = band v305, v327 : i32; - v329 = add.wrapping v278, v328 : i32; - v330 = const.i32 144 : i32; - v331 = add.wrapping v329, v330 : i32; - v332 = cast v278 : u32; - v333 = add.checked v332, 424 : u32; - v334 = inttoptr v333 : *mut i32; - v335 = load v334 : i32; - v336 = cast v278 : u32; - v337 = add.checked v336, 408 : u32; - v338 = inttoptr v337 : *mut i32; - v339 = load v338 : i32; - v340 = const.i32 1 : i32; - v341 = const.i32 3 : i32; - v342 = cast v305 : u32; - v343 = cast v341 : u32; - v344 = shr.wrapping v342, v343 : u32; - v345 = cast v344 : i32; - v346 = shl.wrapping v340, v345 : i32; - v347 = band v339, v346 : i32; - v348 = neq v347, 0 : i1; - condbr v348, block44, block45; - -block36(v266: i32, v279: i32, v280: i32, v287: i32): - v267 = cast v266 : u32; - v268 = add.checked v267, 16 : u32; - v269 = inttoptr v268 : *mut i32; - v270 = load v269 : i32; - v271 = neq v270, 0 : i1; - condbr v271, block38(v270), block39; - -block37: - -block38(v307: i32): - v308 = cast v307 : u32; - v309 = add.checked v308, 4 : u32; - v310 = inttoptr v309 : *mut i32; - v311 = load v310 : i32; - v312 = const.i32 -8 : i32; - v313 = band v311, v312 : i32; - v315 = sub.wrapping v313, v287 : i32; - v317 = cast v315 : u32; - v318 = cast v316 : u32; - v319 = lt v317, v318 : i1; - v320 = cast v319 : i32; - v321 = neq v320, 0 : i1; - v322 = select v321, v315, v280 : i32; - v324 = neq v320, 0 : i1; - v325 = select v324, v307, v279 : i32; - br block36(v307, v325, v322, v314); - -block39: - v272 = const.i32 20 : i32; - v273 = add.wrapping v266, v272 : i32; - v274 = cast v273 : u32; - v275 = inttoptr v274 : *mut i32; - v276 = load v275 : i32; - v277 = neq v276, 0 : i1; - condbr v277, block38(v276), block40; - -block40: - call noname::dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(v278, v279); - v281 = const.i32 16 : i32; - v282 = cast v280 : u32; - v283 = cast v281 : u32; - v284 = lt v282, v283 : i1; - v285 = cast v284 : i32; - v286 = neq v285, 0 : i1; - condbr v286, block34, block41; - -block41: - v288 = const.i32 3 : i32; - v289 = bor v287, v288 : i32; - v290 = cast v279 : u32; - v291 = add.checked v290, 4 : u32; - v292 = inttoptr v291 : *mut i32; - store v292, v289; - v293 = add.wrapping v279, v287 : i32; - v294 = const.i32 1 : i32; - v295 = bor v280, v294 : i32; - v296 = cast v293 : u32; - v297 = add.checked v296, 4 : u32; - v298 = inttoptr v297 : *mut i32; - store v298, v295; - v299 = add.wrapping v293, v280 : i32; - v300 = cast v299 : u32; - v301 = inttoptr v300 : *mut i32; - store v301, v280; - v302 = cast v278 : u32; - v303 = add.checked v302, 416 : u32; - v304 = inttoptr v303 : *mut i32; - v305 = load v304 : i32; - v306 = neq v305, 0 : i1; - condbr v306, block35, block42; - -block42: - br block30; - -block43(v362: i32): - v359 = cast v331 : u32; - v360 = add.checked v359, 8 : u32; - v361 = inttoptr v360 : *mut i32; - store v361, v335; - v363 = cast v362 : u32; - v364 = add.checked v363, 12 : u32; - v365 = inttoptr v364 : *mut i32; - store v365, v358; - v366 = cast v358 : u32; - v367 = add.checked v366, 12 : u32; - v368 = inttoptr v367 : *mut i32; - store v368, v357; - v369 = cast v358 : u32; - v370 = add.checked v369, 8 : u32; - v371 = inttoptr v370 : *mut i32; - store v371, v362; - br block30; - -block44: - v353 = cast v331 : u32; - v354 = add.checked v353, 8 : u32; - v355 = inttoptr v354 : *mut i32; - v356 = load v355 : i32; - br block43(v356); - -block45: - v349 = bor v339, v346 : i32; - v350 = cast v278 : u32; - v351 = add.checked v350, 408 : u32; - v352 = inttoptr v351 : *mut i32; - store v352, v349; - br block43(v331); - -block46: - v426 = const.i32 3 : i32; - v427 = bor v165, v426 : i32; - v428 = cast v404 : u32; - v429 = add.checked v428, 4 : u32; - v430 = inttoptr v429 : *mut i32; - store v430, v427; - v431 = add.wrapping v424, v425 : i32; - v433 = const.i32 3 : i32; - v434 = shl.wrapping v397, v433 : i32; - v435 = sub.wrapping v434, v425 : i32; - v436 = const.i32 1 : i32; - v437 = bor v435, v436 : i32; - v438 = cast v431 : u32; - v439 = add.checked v438, 4 : u32; - v440 = inttoptr v439 : *mut i32; - store v440, v437; - v441 = add.wrapping v424, v434 : i32; - v442 = cast v441 : u32; - v443 = inttoptr v442 : *mut i32; - store v443, v435; - v445 = cast v0 : u32; - v446 = add.checked v445, 416 : u32; - v447 = inttoptr v446 : *mut i32; - v448 = load v447 : i32; - v449 = eq v448, 0 : i1; - v450 = cast v449 : i32; - v451 = neq v450, 0 : i1; - condbr v451, block49, block50; - -block47: - v418 = const.i32 -2 : i32; - v419 = rotl v418, v397 : i32; - v420 = band v153, v419 : i32; - v421 = cast v0 : u32; - v422 = add.checked v421, 408 : u32; - v423 = inttoptr v422 : *mut i32; - store v423, v420; - br block46; - -block48: - v412 = cast v408 : u32; - v413 = add.checked v412, 12 : u32; - v414 = inttoptr v413 : *mut i32; - store v414, v400; - v415 = cast v400 : u32; - v416 = add.checked v415, 8 : u32; - v417 = inttoptr v416 : *mut i32; - store v417, v408; - br block46; - -block49: - v500 = cast v444 : u32; - v501 = add.checked v500, 424 : u32; - v502 = inttoptr v501 : *mut i32; - store v502, v431; - v505 = cast v496 : u32; - v506 = add.checked v505, 416 : u32; - v507 = inttoptr v506 : *mut i32; - store v507, v435; - v510 = const.i32 8 : i32; - v511 = add.wrapping v424, v510 : i32; - ret v511; - -block50: - v453 = const.i32 -8 : i32; - v454 = band v448, v453 : i32; - v455 = add.wrapping v389, v454 : i32; - v456 = cast v444 : u32; - v457 = add.checked v456, 424 : u32; - v458 = inttoptr v457 : *mut i32; - v459 = load v458 : i32; - v460 = cast v444 : u32; - v461 = add.checked v460, 408 : u32; - v462 = inttoptr v461 : *mut i32; - v463 = load v462 : i32; - v464 = const.i32 1 : i32; - v465 = const.i32 3 : i32; - v466 = cast v448 : u32; - v467 = cast v465 : u32; - v468 = shr.wrapping v466, v467 : u32; - v469 = cast v468 : i32; - v470 = shl.wrapping v464, v469 : i32; - v471 = band v463, v470 : i32; - v472 = neq v471, 0 : i1; - condbr v472, block52, block53; - -block51(v486: i32): - v483 = cast v455 : u32; - v484 = add.checked v483, 8 : u32; - v485 = inttoptr v484 : *mut i32; - store v485, v459; - v487 = cast v486 : u32; - v488 = add.checked v487, 12 : u32; - v489 = inttoptr v488 : *mut i32; - store v489, v482; - v490 = cast v482 : u32; - v491 = add.checked v490, 12 : u32; - v492 = inttoptr v491 : *mut i32; - store v492, v481; - v493 = cast v482 : u32; - v494 = add.checked v493, 8 : u32; - v495 = inttoptr v494 : *mut i32; - store v495, v486; - br block49; - -block52: - v477 = cast v455 : u32; - v478 = add.checked v477, 8 : u32; - v479 = inttoptr v478 : *mut i32; - v480 = load v479 : i32; - br block51(v480); - -block53: - v473 = bor v463, v470 : i32; - v474 = cast v444 : u32; - v475 = add.checked v474, 408 : u32; - v476 = inttoptr v475 : *mut i32; - store v476, v473; - br block51(v455); - -block54(v563: i32, v613: i32): - v564 = eq v563, 0 : i1; - v565 = cast v564 : i32; - v566 = neq v565, 0 : i1; - condbr v566, block4(v613, v553, v615, v619), block57; - -block55: - v536 = const.i32 0 : i32; - v537 = const.i32 2 : i32; - v541 = shl.wrapping v537, v538 : i32; - v542 = const.i32 0 : i32; - v543 = sub.wrapping v542, v541 : i32; - v544 = bor v541, v543 : i32; - v549 = band v544, v545 : i32; - v550 = eq v549, 0 : i1; - v551 = cast v550 : i32; - v552 = neq v551, 0 : i1; - condbr v552, block3(v553, v616), block56; - -block56: - v556 = popcnt v549 : i32; - v557 = const.i32 2 : i32; - v558 = shl.wrapping v556, v557 : i32; - v559 = add.wrapping v553, v558 : i32; - v560 = cast v559 : u32; - v561 = inttoptr v560 : *mut i32; - v562 = load v561 : i32; - br block54(v562, v536); - -block57: - br block5(v563, v613, v616, v620, v626); - -block58(v567: i32, v568: i32, v577: i32, v628: i32): - v569 = cast v567 : u32; - v570 = add.checked v569, 4 : u32; - v571 = inttoptr v570 : *mut i32; - v572 = load v571 : i32; - v573 = const.i32 -8 : i32; - v574 = band v572, v573 : i32; - v576 = sub.wrapping v574, v575 : i32; - v578 = cast v576 : u32; - v579 = cast v577 : u32; - v580 = lt v578, v579 : i1; - v581 = cast v580 : i32; - v582 = neq v581, 0 : i1; - v583 = select v582, v567, v568 : i32; - v584 = cast v574 : u32; - v585 = cast v575 : u32; - v586 = lt v584, v585 : i1; - v587 = cast v586 : i32; - v588 = neq v581, 0 : i1; - v589 = select v588, v576, v577 : i32; - v590 = cast v567 : u32; - v591 = add.checked v590, 16 : u32; - v592 = inttoptr v591 : *mut i32; - v593 = load v592 : i32; - v594 = neq v593, 0 : i1; - condbr v594, block60(v593), block61; - -block59: - br block4(v604, v628, v617, v608); - -block60(v609: i32): - v603 = neq v587, 0 : i1; - v604 = select v603, v568, v583 : i32; - v607 = neq v602, 0 : i1; - v608 = select v607, v577, v589 : i32; - v610 = neq v609, 0 : i1; - condbr v610, block58(v609, v604, v608, v627), block62; - -block61: - v595 = const.i32 20 : i32; - v596 = add.wrapping v567, v595 : i32; - v597 = cast v596 : u32; - v598 = inttoptr v597 : *mut i32; - v599 = load v598 : i32; - br block60(v599); - -block62: - br block59; - -block63: - v630 = cast v625 : u32; - v631 = add.checked v630, 416 : u32; - v632 = inttoptr v631 : *mut i32; - v633 = load v632 : i32; - v635 = cast v633 : u32; - v636 = cast v634 : u32; - v637 = lt v635, v636 : i1; - v638 = cast v637 : i32; - v639 = neq v638, 0 : i1; - condbr v639, block64, block65; - -block64: - call noname::dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(v625, v621); - v650 = const.i32 16 : i32; - v651 = cast v640 : u32; - v652 = cast v650 : u32; - v653 = lt v651, v652 : i1; - v654 = cast v653 : i32; - v655 = neq v654, 0 : i1; - condbr v655, block68, block69; - -block65: - v641 = sub.wrapping v633, v634 : i32; - v642 = cast v640 : u32; - v643 = cast v641 : u32; - v644 = gte v642, v643 : i1; - v645 = cast v644 : i32; - v646 = neq v645, 0 : i1; - condbr v646, block3(v625, v634), block66; - -block66: - br block64; - -block67: - v736 = const.i32 8 : i32; - v737 = add.wrapping v648, v736 : i32; - ret v737; - -block68: - v718 = add.wrapping v649, v656 : i32; - v719 = const.i32 3 : i32; - v720 = bor v718, v719 : i32; - v721 = cast v648 : u32; - v722 = add.checked v721, 4 : u32; - v723 = inttoptr v722 : *mut i32; - store v723, v720; - v724 = add.wrapping v648, v718 : i32; - v725 = cast v724 : u32; - v726 = add.checked v725, 4 : u32; - v727 = inttoptr v726 : *mut i32; - v728 = load v727 : i32; - v729 = const.i32 1 : i32; - v730 = bor v728, v729 : i32; - v731 = cast v724 : u32; - v732 = add.checked v731, 4 : u32; - v733 = inttoptr v732 : *mut i32; - store v733, v730; - br block67; - -block69: - v657 = const.i32 3 : i32; - v658 = bor v634, v657 : i32; - v659 = cast v648 : u32; - v660 = add.checked v659, 4 : u32; - v661 = inttoptr v660 : *mut i32; - store v661, v658; - v662 = add.wrapping v648, v656 : i32; - v663 = const.i32 1 : i32; - v664 = bor v649, v663 : i32; - v665 = cast v662 : u32; - v666 = add.checked v665, 4 : u32; - v667 = inttoptr v666 : *mut i32; - store v667, v664; - v668 = add.wrapping v662, v649 : i32; - v669 = cast v668 : u32; - v670 = inttoptr v669 : *mut i32; - store v670, v649; - v671 = const.i32 256 : i32; - v672 = cast v649 : u32; - v673 = cast v671 : u32; - v674 = lt v672, v673 : i1; - v675 = cast v674 : i32; - v676 = neq v675, 0 : i1; - condbr v676, block70, block71; - -block70: - v677 = const.i32 -8 : i32; - v678 = band v649, v677 : i32; - v679 = add.wrapping v647, v678 : i32; - v680 = const.i32 144 : i32; - v681 = add.wrapping v679, v680 : i32; - v682 = cast v647 : u32; - v683 = add.checked v682, 408 : u32; - v684 = inttoptr v683 : *mut i32; - v685 = load v684 : i32; - v686 = const.i32 1 : i32; - v687 = const.i32 3 : i32; - v688 = cast v649 : u32; - v689 = cast v687 : u32; - v690 = shr.wrapping v688, v689 : u32; - v691 = cast v690 : i32; - v692 = shl.wrapping v686, v691 : i32; - v693 = band v685, v692 : i32; - v694 = neq v693, 0 : i1; - condbr v694, block73, block74; - -block71: - call noname::dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk(v647, v662, v649); - br block67; - -block72(v708: i32): - v705 = cast v681 : u32; - v706 = add.checked v705, 8 : u32; - v707 = inttoptr v706 : *mut i32; - store v707, v662; - v709 = cast v708 : u32; - v710 = add.checked v709, 12 : u32; - v711 = inttoptr v710 : *mut i32; - store v711, v704; - v712 = cast v704 : u32; - v713 = add.checked v712, 12 : u32; - v714 = inttoptr v713 : *mut i32; - store v714, v703; - v715 = cast v704 : u32; - v716 = add.checked v715, 8 : u32; - v717 = inttoptr v716 : *mut i32; - store v717, v708; - br block67; - -block73: - v699 = cast v681 : u32; - v700 = add.checked v699, 8 : u32; - v701 = inttoptr v700 : *mut i32; - v702 = load v701 : i32; - br block72(v702); - -block74: - v695 = bor v685, v692 : i32; - v696 = cast v647 : u32; - v697 = add.checked v696, 408 : u32; - v698 = inttoptr v697 : *mut i32; - store v698, v695; - br block72(v681); - -block75(v1519: i32, v1525: i32, v1564: i32): - v1521 = cast v1519 : u32; - v1522 = add.checked v1521, 420 : u32; - v1523 = inttoptr v1522 : *mut i32; - v1524 = load v1523 : i32; - v1534 = cast v1524 : u32; - v1535 = cast v1525 : u32; - v1536 = lte v1534, v1535 : i1; - v1537 = cast v1536 : i32; - v1538 = neq v1537, 0 : i1; - condbr v1538, block2(v1564), block144; - -block76(v1513: i32): - v1517 = const.i32 8 : i32; - v1518 = add.wrapping v1513, v1517 : i32; - ret v1518; - -block77: - v1511 = const.i32 8 : i32; - v1512 = add.wrapping v1381, v1511 : i32; - ret v1512; - -block78: - v1487 = const.i32 0 : i32; - v1488 = cast v738 : u32; - v1489 = add.checked v1488, 424 : u32; - v1490 = inttoptr v1489 : *mut i32; - store v1490, v1487; - v1491 = const.i32 0 : i32; - v1492 = cast v738 : u32; - v1493 = add.checked v1492, 416 : u32; - v1494 = inttoptr v1493 : *mut i32; - store v1494, v1491; - v1495 = const.i32 3 : i32; - v1496 = bor v742, v1495 : i32; - v1497 = cast v1381 : u32; - v1498 = add.checked v1497, 4 : u32; - v1499 = inttoptr v1498 : *mut i32; - store v1499, v1496; - v1500 = add.wrapping v1381, v742 : i32; - v1501 = cast v1500 : u32; - v1502 = add.checked v1501, 4 : u32; - v1503 = inttoptr v1502 : *mut i32; - v1504 = load v1503 : i32; - v1505 = const.i32 1 : i32; - v1506 = bor v1504, v1505 : i32; - v1507 = cast v1500 : u32; - v1508 = add.checked v1507, 4 : u32; - v1509 = inttoptr v1508 : *mut i32; - store v1509, v1506; - br block77; - -block79: - v1468 = cast v1040 : u32; - v1469 = add.checked v1468, 416 : u32; - v1470 = inttoptr v1469 : *mut i32; - v1471 = load v1470 : i32; - v1472 = add.wrapping v1471, v1213 : i32; - v1473 = const.i32 1 : i32; - v1474 = bor v1472, v1473 : i32; - v1475 = cast v1212 : u32; - v1476 = add.checked v1475, 4 : u32; - v1477 = inttoptr v1476 : *mut i32; - store v1477, v1474; - v1478 = cast v1040 : u32; - v1479 = add.checked v1478, 424 : u32; - v1480 = inttoptr v1479 : *mut i32; - store v1480, v1212; - v1481 = cast v1040 : u32; - v1482 = add.checked v1481, 416 : u32; - v1483 = inttoptr v1482 : *mut i32; - store v1483, v1472; - v1484 = add.wrapping v1212, v1472 : i32; - v1485 = cast v1484 : u32; - v1486 = inttoptr v1485 : *mut i32; - store v1486, v1472; - br block76(v1025); - -block80: - v1452 = cast v1040 : u32; - v1453 = add.checked v1452, 428 : u32; - v1454 = inttoptr v1453 : *mut i32; - store v1454, v1212; - v1455 = cast v1040 : u32; - v1456 = add.checked v1455, 420 : u32; - v1457 = inttoptr v1456 : *mut i32; - v1458 = load v1457 : i32; - v1459 = add.wrapping v1458, v1213 : i32; - v1460 = cast v1040 : u32; - v1461 = add.checked v1460, 420 : u32; - v1462 = inttoptr v1461 : *mut i32; - store v1462, v1459; - v1463 = const.i32 1 : i32; - v1464 = bor v1459, v1463 : i32; - v1465 = cast v1212 : u32; - v1466 = add.checked v1465, 4 : u32; - v1467 = inttoptr v1466 : *mut i32; - store v1467, v1464; - br block76(v1025); - -block81: - v1409 = add.wrapping v922, v968 : i32; - v1410 = cast v915 : u32; - v1411 = add.checked v1410, 4 : u32; - v1412 = inttoptr v1411 : *mut i32; - store v1412, v1409; - v1413 = cast v952 : u32; - v1414 = add.checked v1413, 428 : u32; - v1415 = inttoptr v1414 : *mut i32; - v1416 = load v1415 : i32; - v1417 = const.i32 15 : i32; - v1418 = add.wrapping v1416, v1417 : i32; - v1419 = const.i32 -8 : i32; - v1420 = band v1418, v1419 : i32; - v1421 = const.i32 -8 : i32; - v1422 = add.wrapping v1420, v1421 : i32; - v1423 = sub.wrapping v1416, v1420 : i32; - v1424 = cast v952 : u32; - v1425 = add.checked v1424, 420 : u32; - v1426 = inttoptr v1425 : *mut i32; - v1427 = load v1426 : i32; - v1428 = add.wrapping v1427, v968 : i32; - v1429 = add.wrapping v1423, v1428 : i32; - v1430 = const.i32 8 : i32; - v1431 = add.wrapping v1429, v1430 : i32; - v1432 = const.i32 1 : i32; - v1433 = bor v1431, v1432 : i32; - v1434 = cast v1422 : u32; - v1435 = add.checked v1434, 4 : u32; - v1436 = inttoptr v1435 : *mut i32; - store v1436, v1433; - v1437 = const.i32 2097152 : i32; - v1438 = cast v952 : u32; - v1439 = add.checked v1438, 440 : u32; - v1440 = inttoptr v1439 : *mut i32; - store v1440, v1437; - v1441 = cast v952 : u32; - v1442 = add.checked v1441, 428 : u32; - v1443 = inttoptr v1442 : *mut i32; - store v1443, v1422; - v1444 = cast v952 : u32; - v1445 = add.checked v1444, 420 : u32; - v1446 = inttoptr v1445 : *mut i32; - store v1446, v1431; - v1447 = add.wrapping v1416, v1428 : i32; - v1448 = const.i32 40 : i32; - v1449 = cast v1447 : u32; - v1450 = add.checked v1449, 4 : u32; - v1451 = inttoptr v1450 : *mut i32; - store v1451, v1448; - br block75(v952, v1206, v1573); - -block82: - v1378 = cast v738 : u32; - v1379 = add.checked v1378, 424 : u32; - v1380 = inttoptr v1379 : *mut i32; - v1381 = load v1380 : i32; - v1382 = sub.wrapping v742, v743 : i32; - v1383 = const.i32 16 : i32; - v1384 = cast v1382 : u32; - v1385 = cast v1383 : u32; - v1386 = lt v1384, v1385 : i1; - v1387 = cast v1386 : i32; - v1388 = neq v1387, 0 : i1; - condbr v1388, block78, block143; - -block83: - v749 = cast v738 : u32; - v750 = add.checked v749, 420 : u32; - v751 = inttoptr v750 : *mut i32; - v752 = load v751 : i32; - v753 = cast v752 : u32; - v754 = cast v743 : u32; - v755 = gt v753, v754 : i1; - v756 = cast v755 : i32; - v757 = neq v756, 0 : i1; - condbr v757, block84, block85; - -block84: - v1354 = sub.wrapping v752, v743 : i32; - v1355 = cast v738 : u32; - v1356 = add.checked v1355, 420 : u32; - v1357 = inttoptr v1356 : *mut i32; - store v1357, v1354; - v1358 = cast v738 : u32; - v1359 = add.checked v1358, 428 : u32; - v1360 = inttoptr v1359 : *mut i32; - v1361 = load v1360 : i32; - v1362 = add.wrapping v1361, v743 : i32; - v1363 = cast v738 : u32; - v1364 = add.checked v1363, 428 : u32; - v1365 = inttoptr v1364 : *mut i32; - store v1365, v1362; - v1366 = const.i32 1 : i32; - v1367 = bor v1354, v1366 : i32; - v1368 = cast v1362 : u32; - v1369 = add.checked v1368, 4 : u32; - v1370 = inttoptr v1369 : *mut i32; - store v1370, v1367; - v1371 = const.i32 3 : i32; - v1372 = bor v743, v1371 : i32; - v1373 = cast v1361 : u32; - v1374 = add.checked v1373, 4 : u32; - v1375 = inttoptr v1374 : *mut i32; - store v1375, v1372; - v1376 = const.i32 8 : i32; - v1377 = add.wrapping v1361, v1376 : i32; - br block2(v1377); - -block85: - v758 = const.i32 0 : i32; - v759 = const.i32 65583 : i32; - v760 = add.wrapping v743, v759 : i32; - v761 = const.i32 16 : i32; - v762 = cast v760 : u32; - v763 = cast v761 : u32; - v764 = shr.wrapping v762, v763 : u32; - v765 = cast v764 : i32; - v766 = cast v765 : u32; - v767 = memory.grow v766 : i32; - v768 = const.i32 -1 : i32; - v769 = eq v767, v768 : i1; - v770 = cast v769 : i32; - v771 = neq v770, 0 : i1; - condbr v771, block2(v758), block86; - -block86: - v772 = const.i32 16 : i32; - v773 = shl.wrapping v767, v772 : i32; - v774 = eq v773, 0 : i1; - v775 = cast v774 : i32; - v776 = neq v775, 0 : i1; - condbr v776, block2(v758), block87; - -block87: - v777 = cast v738 : u32; - v778 = add.checked v777, 432 : u32; - v779 = inttoptr v778 : *mut i32; - v780 = load v779 : i32; - v781 = const.i32 0 : i32; - v782 = const.i32 -65536 : i32; - v783 = band v760, v782 : i32; - v784 = neq v770, 0 : i1; - v785 = select v784, v781, v783 : i32; - v786 = add.wrapping v780, v785 : i32; - v787 = cast v738 : u32; - v788 = add.checked v787, 432 : u32; - v789 = inttoptr v788 : *mut i32; - store v789, v786; - v790 = cast v738 : u32; - v791 = add.checked v790, 436 : u32; - v792 = inttoptr v791 : *mut i32; - v793 = load v792 : i32; - v794 = cast v793 : u32; - v795 = cast v786 : u32; - v796 = gt v794, v795 : i1; - v797 = cast v796 : i32; - v798 = neq v797, 0 : i1; - v799 = select v798, v793, v786 : i32; - v800 = cast v738 : u32; - v801 = add.checked v800, 436 : u32; - v802 = inttoptr v801 : *mut i32; - store v802, v799; - v803 = cast v738 : u32; - v804 = add.checked v803, 428 : u32; - v805 = inttoptr v804 : *mut i32; - v806 = load v805 : i32; - v807 = neq v806, 0 : i1; - condbr v807, block88, block89; - -block88: - v913 = const.i32 128 : i32; - v914 = add.wrapping v738, v913 : i32; - br block99(v914); - -block89: - v808 = cast v738 : u32; - v809 = add.checked v808, 444 : u32; - v810 = inttoptr v809 : *mut i32; - v811 = load v810 : i32; - v812 = eq v811, 0 : i1; - v813 = cast v812 : i32; - v814 = neq v813, 0 : i1; - condbr v814, block91, block92; - -block90(v825: i32, v830: i32): - v826 = const.i32 4095 : i32; - v827 = cast v825 : u32; - v828 = add.checked v827, 448 : u32; - v829 = inttoptr v828 : *mut i32; - store v829, v826; - v831 = cast v825 : u32; - v832 = add.checked v831, 128 : u32; - v833 = inttoptr v832 : *mut i32; - store v833, v830; - v834 = const.i32 0 : i32; - v835 = const.i32 140 : i32; - v836 = add.wrapping v825, v835 : i32; - v837 = const.i32 0 : i32; - v838 = cast v836 : u32; - v839 = inttoptr v838 : *mut i32; - store v839, v837; - v840 = const.i32 132 : i32; - v841 = add.wrapping v825, v840 : i32; - v844 = cast v841 : u32; - v845 = inttoptr v844 : *mut i32; - store v845, v785; - br block94(v834); - -block91: - v822 = cast v738 : u32; - v823 = add.checked v822, 444 : u32; - v824 = inttoptr v823 : *mut i32; - store v824, v773; - br block90(v820, v821); - -block92: - v815 = cast v811 : u32; - v816 = cast v773 : u32; - v817 = lte v815, v816 : i1; - v818 = cast v817 : i32; - v819 = neq v818, 0 : i1; - condbr v819, block90(v738, v773), block93; - -block93: - br block91; - -block94(v847: i32): - v848 = add.wrapping v846, v847 : i32; - v849 = const.i32 164 : i32; - v850 = add.wrapping v848, v849 : i32; - v851 = const.i32 152 : i32; - v852 = add.wrapping v848, v851 : i32; - v853 = cast v850 : u32; - v854 = inttoptr v853 : *mut i32; - store v854, v852; - v855 = const.i32 144 : i32; - v856 = add.wrapping v848, v855 : i32; - v857 = cast v852 : u32; - v858 = inttoptr v857 : *mut i32; - store v858, v856; - v859 = const.i32 156 : i32; - v860 = add.wrapping v848, v859 : i32; - v861 = cast v860 : u32; - v862 = inttoptr v861 : *mut i32; - store v862, v856; - v863 = const.i32 172 : i32; - v864 = add.wrapping v848, v863 : i32; - v865 = const.i32 160 : i32; - v866 = add.wrapping v848, v865 : i32; - v867 = cast v864 : u32; - v868 = inttoptr v867 : *mut i32; - store v868, v866; - v869 = cast v866 : u32; - v870 = inttoptr v869 : *mut i32; - store v870, v852; - v871 = const.i32 180 : i32; - v872 = add.wrapping v848, v871 : i32; - v873 = const.i32 168 : i32; - v874 = add.wrapping v848, v873 : i32; - v875 = cast v872 : u32; - v876 = inttoptr v875 : *mut i32; - store v876, v874; - v877 = cast v874 : u32; - v878 = inttoptr v877 : *mut i32; - store v878, v866; - v879 = const.i32 176 : i32; - v880 = add.wrapping v848, v879 : i32; - v881 = cast v880 : u32; - v882 = inttoptr v881 : *mut i32; - store v882, v874; - v883 = const.i32 32 : i32; - v884 = add.wrapping v847, v883 : i32; - v885 = const.i32 256 : i32; - v886 = neq v884, v885 : i1; - v887 = cast v886 : i32; - v888 = neq v887, 0 : i1; - condbr v888, block94(v884), block96; - -block95: - v891 = const.i32 -40 : i32; - v892 = add.wrapping v842, v891 : i32; - v893 = const.i32 1 : i32; - v894 = bor v892, v893 : i32; - v895 = cast v830 : u32; - v896 = add.checked v895, 4 : u32; - v897 = inttoptr v896 : *mut i32; - store v897, v894; - v898 = cast v846 : u32; - v899 = add.checked v898, 428 : u32; - v900 = inttoptr v899 : *mut i32; - store v900, v889; - v901 = const.i32 2097152 : i32; - v902 = cast v846 : u32; - v903 = add.checked v902, 440 : u32; - v904 = inttoptr v903 : *mut i32; - store v904, v901; - v905 = cast v846 : u32; - v906 = add.checked v905, 420 : u32; - v907 = inttoptr v906 : *mut i32; - store v907, v892; - v908 = add.wrapping v889, v892 : i32; - v909 = const.i32 40 : i32; - v910 = cast v908 : u32; - v911 = add.checked v910, 4 : u32; - v912 = inttoptr v911 : *mut i32; - store v912, v909; - br block75(v846, v743, v758); - -block96: - br block95; - -block97(v951: i32, v967: i32, v970: i32, v1205: i32, v1572: i32): - v953 = cast v951 : u32; - v954 = add.checked v953, 444 : u32; - v955 = inttoptr v954 : *mut i32; - v956 = load v955 : i32; - v958 = cast v956 : u32; - v959 = cast v957 : u32; - v960 = lt v958, v959 : i1; - v961 = cast v960 : i32; - v962 = neq v961, 0 : i1; - v963 = select v962, v956, v924 : i32; - v964 = cast v951 : u32; - v965 = add.checked v964, 444 : u32; - v966 = inttoptr v965 : *mut i32; - store v966, v963; - v969 = add.wrapping v957, v967 : i32; - br block109(v970); - -block98: - v934 = cast v806 : u32; - v935 = cast v924 : u32; - v936 = gte v934, v935 : i1; - v937 = cast v936 : i32; - v938 = neq v937, 0 : i1; - condbr v938, block97(v952, v968, v971, v1206, v1573), block103; - -block99(v915: i32): - v916 = cast v915 : u32; - v917 = inttoptr v916 : *mut i32; - v918 = load v917 : i32; - v919 = cast v915 : u32; - v920 = add.checked v919, 4 : u32; - v921 = inttoptr v920 : *mut i32; - v922 = load v921 : i32; - v923 = add.wrapping v918, v922 : i32; - v925 = eq v923, v924 : i1; - v926 = cast v925 : i32; - v927 = neq v926, 0 : i1; - condbr v927, block98, block101; - -block100: - -block101: - v928 = cast v915 : u32; - v929 = add.checked v928, 8 : u32; - v930 = inttoptr v929 : *mut i32; - v931 = load v930 : i32; - v932 = neq v931, 0 : i1; - condbr v932, block99(v931), block102; - -block102: - br block97(v738, v785, v914, v743, v758); - -block103: - v939 = cast v918 : u32; - v940 = cast v933 : u32; - v941 = gt v939, v940 : i1; - v942 = cast v941 : i32; - v943 = neq v942, 0 : i1; - condbr v943, block97(v952, v968, v971, v1206, v1573), block104; - -block104: - v944 = cast v915 : u32; - v945 = add.checked v944, 12 : u32; - v946 = inttoptr v945 : *mut i32; - v947 = load v946 : i32; - v948 = eq v947, 0 : i1; - v949 = cast v948 : i32; - v950 = neq v949, 0 : i1; - condbr v950, block81, block105; - -block105: - br block97(v952, v968, v971, v1206, v1573); - -block106: - v1194 = cast v972 : u32; - v1195 = inttoptr v1194 : *mut i32; - store v1195, v1025; - v1196 = cast v972 : u32; - v1197 = add.checked v1196, 4 : u32; - v1198 = inttoptr v1197 : *mut i32; - v1199 = load v1198 : i32; - v1200 = add.wrapping v1199, v1029 : i32; - v1201 = cast v972 : u32; - v1202 = add.checked v1201, 4 : u32; - v1203 = inttoptr v1202 : *mut i32; - store v1203, v1200; - v1207 = const.i32 3 : i32; - v1208 = bor v1205, v1207 : i32; - v1209 = cast v1025 : u32; - v1210 = add.checked v1209, 4 : u32; - v1211 = inttoptr v1210 : *mut i32; - store v1211, v1208; - v1212 = add.wrapping v1025, v1204 : i32; - v1213 = sub.wrapping v976, v1212 : i32; - v1214 = cast v1040 : u32; - v1215 = add.checked v1214, 428 : u32; - v1216 = inttoptr v1215 : *mut i32; - v1217 = load v1216 : i32; - v1218 = eq v976, v1217 : i1; - v1219 = cast v1218 : i32; - v1220 = neq v1219, 0 : i1; - condbr v1220, block80, block129; - -block107(v992: i32, v1019: i32, v1024: i32, v1028: i32, v1039: i32, v1570: i32): - br block115(v992); - -block108: - v985 = cast v972 : u32; - v986 = add.checked v985, 12 : u32; - v987 = inttoptr v986 : *mut i32; - v988 = load v987 : i32; - v989 = eq v988, 0 : i1; - v990 = cast v989 : i32; - v991 = neq v990, 0 : i1; - condbr v991, block106, block113; - -block109(v972: i32): - v973 = cast v972 : u32; - v974 = inttoptr v973 : *mut i32; - v975 = load v974 : i32; - v977 = eq v975, v976 : i1; - v978 = cast v977 : i32; - v979 = neq v978, 0 : i1; - condbr v979, block108, block111; - -block110: - -block111: - v980 = cast v972 : u32; - v981 = add.checked v980, 8 : u32; - v982 = inttoptr v981 : *mut i32; - v983 = load v982 : i32; - v984 = neq v983, 0 : i1; - condbr v984, block109(v983), block112; - -block112: - br block107(v970, v933, v957, v967, v951, v1572); - -block113: - br block107(v993, v1020, v1025, v1029, v1040, v1571); - -block114: - v1031 = const.i32 -40 : i32; - v1032 = add.wrapping v1028, v1031 : i32; - v1033 = const.i32 1 : i32; - v1034 = bor v1032, v1033 : i32; - v1035 = cast v1024 : u32; - v1036 = add.checked v1035, 4 : u32; - v1037 = inttoptr v1036 : *mut i32; - store v1037, v1034; - v1042 = cast v1039 : u32; - v1043 = add.checked v1042, 428 : u32; - v1044 = inttoptr v1043 : *mut i32; - store v1044, v1023; - v1045 = const.i32 2097152 : i32; - v1046 = cast v1038 : u32; - v1047 = add.checked v1046, 440 : u32; - v1048 = inttoptr v1047 : *mut i32; - store v1048, v1045; - v1049 = cast v1038 : u32; - v1050 = add.checked v1049, 420 : u32; - v1051 = inttoptr v1050 : *mut i32; - store v1051, v1032; - v1052 = add.wrapping v1023, v1032 : i32; - v1053 = const.i32 40 : i32; - v1054 = cast v1052 : u32; - v1055 = add.checked v1054, 4 : u32; - v1056 = inttoptr v1055 : *mut i32; - store v1056, v1053; - v1057 = const.i32 -32 : i32; - v1058 = add.wrapping v1008, v1057 : i32; - v1059 = const.i32 -8 : i32; - v1060 = band v1058, v1059 : i32; - v1061 = const.i32 -8 : i32; - v1062 = add.wrapping v1060, v1061 : i32; - v1063 = const.i32 16 : i32; - v1064 = add.wrapping v998, v1063 : i32; - v1065 = cast v1062 : u32; - v1066 = cast v1064 : u32; - v1067 = lt v1065, v1066 : i1; - v1068 = cast v1067 : i32; - v1069 = neq v1068, 0 : i1; - v1070 = select v1069, v998, v1062 : i32; - v1071 = const.i32 27 : i32; - v1072 = cast v1070 : u32; - v1073 = add.checked v1072, 4 : u32; - v1074 = inttoptr v1073 : *mut i32; - store v1074, v1071; - v1077 = cast v992 : u32; - v1078 = inttoptr v1077 : *mut i64; - v1079 = load v1078 : i64; - v1080 = const.i32 16 : i32; - v1081 = add.wrapping v1070, v1080 : i32; - v1082 = const.i32 8 : i32; - v1083 = add.wrapping v1075, v1082 : i32; - v1084 = cast v1083 : u32; - v1085 = inttoptr v1084 : *mut i64; - v1086 = load v1085 : i64; - v1087 = cast v1081 : u32; - v1088 = inttoptr v1087 : *mut i64; - store v1088, v1086; - v1089 = cast v1070 : u32; - v1090 = add.checked v1089, 8 : u32; - v1091 = inttoptr v1090 : *mut i64; - store v1091, v1079; - v1092 = const.i32 140 : i32; - v1093 = add.wrapping v1038, v1092 : i32; - v1094 = const.i32 0 : i32; - v1095 = cast v1093 : u32; - v1096 = inttoptr v1095 : *mut i32; - store v1096, v1094; - v1097 = const.i32 132 : i32; - v1098 = add.wrapping v1038, v1097 : i32; - v1099 = cast v1098 : u32; - v1100 = inttoptr v1099 : *mut i32; - store v1100, v1027; - v1101 = cast v1038 : u32; - v1102 = add.checked v1101, 128 : u32; - v1103 = inttoptr v1102 : *mut i32; - store v1103, v1023; - v1104 = const.i32 136 : i32; - v1105 = add.wrapping v1038, v1104 : i32; - v1106 = const.i32 8 : i32; - v1107 = add.wrapping v1070, v1106 : i32; - v1108 = cast v1105 : u32; - v1109 = inttoptr v1108 : *mut i32; - store v1109, v1107; - v1110 = const.i32 28 : i32; - v1111 = add.wrapping v1070, v1110 : i32; - br block120(v1111); - -block115(v994: i32): - v995 = cast v994 : u32; - v996 = inttoptr v995 : *mut i32; - v997 = load v996 : i32; - v999 = cast v997 : u32; - v1000 = cast v998 : u32; - v1001 = gt v999, v1000 : i1; - v1002 = cast v1001 : i32; - v1003 = neq v1002, 0 : i1; - condbr v1003, block117, block118; - -block116: - -block117: - v1015 = cast v994 : u32; - v1016 = add.checked v1015, 8 : u32; - v1017 = inttoptr v1016 : *mut i32; - v1018 = load v1017 : i32; - br block115(v1018); - -block118: - v1004 = cast v994 : u32; - v1005 = add.checked v1004, 4 : u32; - v1006 = inttoptr v1005 : *mut i32; - v1007 = load v1006 : i32; - v1008 = add.wrapping v997, v1007 : i32; - v1009 = cast v1008 : u32; - v1010 = cast v998 : u32; - v1011 = gt v1009, v1010 : i1; - v1012 = cast v1011 : i32; - v1013 = neq v1012, 0 : i1; - condbr v1013, block114, block119; - -block119: - br block117; - -block120(v1112: i32): - v1113 = const.i32 7 : i32; - v1114 = cast v1112 : u32; - v1115 = inttoptr v1114 : *mut i32; - store v1115, v1113; - v1116 = const.i32 4 : i32; - v1117 = add.wrapping v1112, v1116 : i32; - v1119 = cast v1117 : u32; - v1120 = cast v1118 : u32; - v1121 = lt v1119, v1120 : i1; - v1122 = cast v1121 : i32; - v1123 = neq v1122, 0 : i1; - condbr v1123, block120(v1117), block122; - -block121: - v1126 = eq v1070, v998 : i1; - v1127 = cast v1126 : i32; - v1128 = neq v1127, 0 : i1; - condbr v1128, block75(v1152, v1204, v1570), block123; - -block122: - br block121; - -block123: - v1129 = cast v1124 : u32; - v1130 = add.checked v1129, 4 : u32; - v1131 = inttoptr v1130 : *mut i32; - v1132 = load v1131 : i32; - v1133 = const.i32 -2 : i32; - v1134 = band v1132, v1133 : i32; - v1135 = cast v1124 : u32; - v1136 = add.checked v1135, 4 : u32; - v1137 = inttoptr v1136 : *mut i32; - store v1137, v1134; - v1138 = sub.wrapping v1124, v1125 : i32; - v1139 = const.i32 1 : i32; - v1140 = bor v1138, v1139 : i32; - v1141 = cast v1125 : u32; - v1142 = add.checked v1141, 4 : u32; - v1143 = inttoptr v1142 : *mut i32; - store v1143, v1140; - v1144 = cast v1124 : u32; - v1145 = inttoptr v1144 : *mut i32; - store v1145, v1138; - v1146 = const.i32 256 : i32; - v1147 = cast v1138 : u32; - v1148 = cast v1146 : u32; - v1149 = lt v1147, v1148 : i1; - v1150 = cast v1149 : i32; - v1151 = neq v1150, 0 : i1; - condbr v1151, block124, block125; - -block124: - v1153 = const.i32 -8 : i32; - v1154 = band v1138, v1153 : i32; - v1155 = add.wrapping v1152, v1154 : i32; - v1156 = const.i32 144 : i32; - v1157 = add.wrapping v1155, v1156 : i32; - v1158 = cast v1152 : u32; - v1159 = add.checked v1158, 408 : u32; - v1160 = inttoptr v1159 : *mut i32; - v1161 = load v1160 : i32; - v1162 = const.i32 1 : i32; - v1163 = const.i32 3 : i32; - v1164 = cast v1138 : u32; - v1165 = cast v1163 : u32; - v1166 = shr.wrapping v1164, v1165 : u32; - v1167 = cast v1166 : i32; - v1168 = shl.wrapping v1162, v1167 : i32; - v1169 = band v1161, v1168 : i32; - v1170 = neq v1169, 0 : i1; - condbr v1170, block127, block128; - -block125: - call noname::dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk(v1038, v1125, v1138); - br block75(v1152, v1529, v1568); - -block126(v1184: i32): - v1181 = cast v1157 : u32; - v1182 = add.checked v1181, 8 : u32; - v1183 = inttoptr v1182 : *mut i32; - store v1183, v1125; - v1185 = cast v1184 : u32; - v1186 = add.checked v1185, 12 : u32; - v1187 = inttoptr v1186 : *mut i32; - store v1187, v1180; - v1188 = cast v1180 : u32; - v1189 = add.checked v1188, 12 : u32; - v1190 = inttoptr v1189 : *mut i32; - store v1190, v1179; - v1191 = cast v1180 : u32; - v1192 = add.checked v1191, 8 : u32; - v1193 = inttoptr v1192 : *mut i32; - store v1193, v1184; - br block75(v1152, v1529, v1568); - -block127: - v1175 = cast v1157 : u32; - v1176 = add.checked v1175, 8 : u32; - v1177 = inttoptr v1176 : *mut i32; - v1178 = load v1177 : i32; - br block126(v1178); - -block128: - v1171 = bor v1161, v1168 : i32; - v1172 = cast v1152 : u32; - v1173 = add.checked v1172, 408 : u32; - v1174 = inttoptr v1173 : *mut i32; - store v1174, v1171; - br block126(v1157); - -block129: - v1221 = cast v1040 : u32; - v1222 = add.checked v1221, 424 : u32; - v1223 = inttoptr v1222 : *mut i32; - v1224 = load v1223 : i32; - v1225 = eq v976, v1224 : i1; - v1226 = cast v1225 : i32; - v1227 = neq v1226, 0 : i1; - condbr v1227, block79, block130; - -block130: - v1228 = cast v976 : u32; - v1229 = add.checked v1228, 4 : u32; - v1230 = inttoptr v1229 : *mut i32; - v1231 = load v1230 : i32; - v1232 = const.i32 3 : i32; - v1233 = band v1231, v1232 : i32; - v1234 = const.i32 1 : i32; - v1235 = neq v1233, v1234 : i1; - v1236 = cast v1235 : i32; - v1237 = neq v1236, 0 : i1; - condbr v1237, block131(v976, v1231, v1213), block132; - -block131(v1287: i32, v1288: i32, v1296: i32): - v1289 = const.i32 -2 : i32; - v1290 = band v1288, v1289 : i32; - v1291 = cast v1287 : u32; - v1292 = add.checked v1291, 4 : u32; - v1293 = inttoptr v1292 : *mut i32; - store v1293, v1290; - v1297 = const.i32 1 : i32; - v1298 = bor v1296, v1297 : i32; - v1299 = cast v1212 : u32; - v1300 = add.checked v1299, 4 : u32; - v1301 = inttoptr v1300 : *mut i32; - store v1301, v1298; - v1302 = add.wrapping v1294, v1296 : i32; - v1303 = cast v1302 : u32; - v1304 = inttoptr v1303 : *mut i32; - store v1304, v1296; - v1305 = const.i32 256 : i32; - v1306 = cast v1296 : u32; - v1307 = cast v1305 : u32; - v1308 = lt v1306, v1307 : i1; - v1309 = cast v1308 : i32; - v1310 = neq v1309, 0 : i1; - condbr v1310, block138, block139; - -block132: - v1238 = const.i32 -8 : i32; - v1239 = band v1231, v1238 : i32; - v1240 = const.i32 256 : i32; - v1241 = cast v1239 : u32; - v1242 = cast v1240 : u32; - v1243 = lt v1241, v1242 : i1; - v1244 = cast v1243 : i32; - v1245 = neq v1244, 0 : i1; - condbr v1245, block134, block135; - -block133: - v1280 = add.wrapping v1239, v1213 : i32; - v1282 = add.wrapping v976, v1278 : i32; - v1283 = cast v1282 : u32; - v1284 = add.checked v1283, 4 : u32; - v1285 = inttoptr v1284 : *mut i32; - v1286 = load v1285 : i32; - br block131(v1282, v1286, v1280); - -block134: - v1246 = cast v976 : u32; - v1247 = add.checked v1246, 12 : u32; - v1248 = inttoptr v1247 : *mut i32; - v1249 = load v1248 : i32; - v1250 = cast v976 : u32; - v1251 = add.checked v1250, 8 : u32; - v1252 = inttoptr v1251 : *mut i32; - v1253 = load v1252 : i32; - v1254 = eq v1249, v1253 : i1; - v1255 = cast v1254 : i32; - v1256 = neq v1255, 0 : i1; - condbr v1256, block136, block137; - -block135: - call noname::dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(v1040, v976); - br block133; - -block136: - v1263 = cast v1040 : u32; - v1264 = add.checked v1263, 408 : u32; - v1265 = inttoptr v1264 : *mut i32; - v1266 = load v1265 : i32; - v1267 = const.i32 -2 : i32; - v1268 = const.i32 3 : i32; - v1269 = cast v1231 : u32; - v1270 = cast v1268 : u32; - v1271 = shr.wrapping v1269, v1270 : u32; - v1272 = cast v1271 : i32; - v1273 = rotl v1267, v1272 : i32; - v1274 = band v1266, v1273 : i32; - v1275 = cast v1040 : u32; - v1276 = add.checked v1275, 408 : u32; - v1277 = inttoptr v1276 : *mut i32; - store v1277, v1274; - br block133; - -block137: - v1257 = cast v1253 : u32; - v1258 = add.checked v1257, 12 : u32; - v1259 = inttoptr v1258 : *mut i32; - store v1259, v1249; - v1260 = cast v1249 : u32; - v1261 = add.checked v1260, 8 : u32; - v1262 = inttoptr v1261 : *mut i32; - store v1262, v1253; - br block133; - -block138: - v1313 = const.i32 -8 : i32; - v1314 = band v1296, v1313 : i32; - v1315 = add.wrapping v1311, v1314 : i32; - v1316 = const.i32 144 : i32; - v1317 = add.wrapping v1315, v1316 : i32; - v1318 = cast v1311 : u32; - v1319 = add.checked v1318, 408 : u32; - v1320 = inttoptr v1319 : *mut i32; - v1321 = load v1320 : i32; - v1322 = const.i32 1 : i32; - v1323 = const.i32 3 : i32; - v1324 = cast v1296 : u32; - v1325 = cast v1323 : u32; - v1326 = shr.wrapping v1324, v1325 : u32; - v1327 = cast v1326 : i32; - v1328 = shl.wrapping v1322, v1327 : i32; - v1329 = band v1321, v1328 : i32; - v1330 = neq v1329, 0 : i1; - condbr v1330, block141, block142; - -block139: - call noname::dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk(v1040, v1294, v1296); - br block76(v1025); - -block140(v1344: i32): - v1341 = cast v1317 : u32; - v1342 = add.checked v1341, 8 : u32; - v1343 = inttoptr v1342 : *mut i32; - store v1343, v1294; - v1345 = cast v1344 : u32; - v1346 = add.checked v1345, 12 : u32; - v1347 = inttoptr v1346 : *mut i32; - store v1347, v1340; - v1348 = cast v1340 : u32; - v1349 = add.checked v1348, 12 : u32; - v1350 = inttoptr v1349 : *mut i32; - store v1350, v1339; - v1351 = cast v1340 : u32; - v1352 = add.checked v1351, 8 : u32; - v1353 = inttoptr v1352 : *mut i32; - store v1353, v1344; - br block76(v1514); - -block141: - v1335 = cast v1317 : u32; - v1336 = add.checked v1335, 8 : u32; - v1337 = inttoptr v1336 : *mut i32; - v1338 = load v1337 : i32; - br block140(v1338); - -block142: - v1331 = bor v1321, v1328 : i32; - v1332 = cast v1311 : u32; - v1333 = add.checked v1332, 408 : u32; - v1334 = inttoptr v1333 : *mut i32; - store v1334, v1331; - br block140(v1317); - -block143: - v1389 = cast v738 : u32; - v1390 = add.checked v1389, 416 : u32; - v1391 = inttoptr v1390 : *mut i32; - store v1391, v1382; - v1392 = add.wrapping v1381, v743 : i32; - v1393 = cast v738 : u32; - v1394 = add.checked v1393, 424 : u32; - v1395 = inttoptr v1394 : *mut i32; - store v1395, v1392; - v1396 = const.i32 1 : i32; - v1397 = bor v1382, v1396 : i32; - v1398 = cast v1392 : u32; - v1399 = add.checked v1398, 4 : u32; - v1400 = inttoptr v1399 : *mut i32; - store v1400, v1397; - v1401 = add.wrapping v1381, v742 : i32; - v1402 = cast v1401 : u32; - v1403 = inttoptr v1402 : *mut i32; - store v1403, v1382; - v1404 = const.i32 3 : i32; - v1405 = bor v743, v1404 : i32; - v1406 = cast v1381 : u32; - v1407 = add.checked v1406, 4 : u32; - v1408 = inttoptr v1407 : *mut i32; - store v1408, v1405; - br block77; - -block144: - v1539 = sub.wrapping v1524, v1525 : i32; - v1540 = cast v1519 : u32; - v1541 = add.checked v1540, 420 : u32; - v1542 = inttoptr v1541 : *mut i32; - store v1542, v1539; - v1543 = cast v1519 : u32; - v1544 = add.checked v1543, 428 : u32; - v1545 = inttoptr v1544 : *mut i32; - v1546 = load v1545 : i32; - v1547 = add.wrapping v1546, v1525 : i32; - v1548 = cast v1519 : u32; - v1549 = add.checked v1548, 428 : u32; - v1550 = inttoptr v1549 : *mut i32; - store v1550, v1547; - v1551 = const.i32 1 : i32; - v1552 = bor v1539, v1551 : i32; - v1553 = cast v1547 : u32; - v1554 = add.checked v1553, 4 : u32; - v1555 = inttoptr v1554 : *mut i32; - store v1555, v1552; - v1556 = const.i32 3 : i32; - v1557 = bor v1525, v1556 : i32; - v1558 = cast v1546 : u32; - v1559 = add.checked v1558, 4 : u32; - v1560 = inttoptr v1559 : *mut i32; - store v1560, v1557; - v1561 = const.i32 8 : i32; - v1562 = add.wrapping v1546, v1561 : i32; - ret v1562; -} - -pub fn dlmalloc::dlmalloc::Dlmalloc::memalign(i32, i32, i32) -> i32 { -block0(v0: i32, v1: i32, v2: i32): - v4 = const.i32 0 : i32; - v5 = const.i32 0 : i32; - v6 = const.i32 -65587 : i32; - v7 = const.i32 16 : i32; - v8 = const.i32 16 : i32; - v9 = cast v1 : u32; - v10 = cast v8 : u32; - v11 = gt v9, v10 : i1; - v12 = cast v11 : i32; - v13 = neq v12, 0 : i1; - v14 = select v13, v1, v7 : i32; - v15 = sub.wrapping v6, v14 : i32; - v16 = cast v15 : u32; - v17 = cast v2 : u32; - v18 = lte v16, v17 : i1; - v19 = cast v18 : i32; - v20 = neq v19, 0 : i1; - condbr v20, block2(v5), block3; - -block1(v3: i32): - ret v3; - -block2(v176: i32): - br block1(v176); - -block3: - v21 = const.i32 16 : i32; - v22 = const.i32 11 : i32; - v23 = add.wrapping v2, v22 : i32; - v24 = const.i32 -8 : i32; - v25 = band v23, v24 : i32; - v26 = const.i32 11 : i32; - v27 = cast v2 : u32; - v28 = cast v26 : u32; - v29 = lt v27, v28 : i1; - v30 = cast v29 : i32; - v31 = neq v30, 0 : i1; - v32 = select v31, v21, v25 : i32; - v33 = add.wrapping v14, v32 : i32; - v34 = const.i32 12 : i32; - v35 = add.wrapping v33, v34 : i32; - v36 = call noname::dlmalloc::dlmalloc::Dlmalloc::malloc(v0, v35) : i32; - v37 = eq v36, 0 : i1; - v38 = cast v37 : i32; - v39 = neq v38, 0 : i1; - condbr v39, block2(v5), block4; - -block4: - v40 = const.i32 -8 : i32; - v41 = add.wrapping v36, v40 : i32; - v42 = const.i32 -1 : i32; - v43 = add.wrapping v14, v42 : i32; - v44 = band v43, v36 : i32; - v45 = neq v44, 0 : i1; - condbr v45, block6, block7; - -block5(v127: i32): - v128 = cast v127 : u32; - v129 = add.checked v128, 4 : u32; - v130 = inttoptr v129 : *mut i32; - v131 = load v130 : i32; - v132 = const.i32 3 : i32; - v133 = band v131, v132 : i32; - v134 = eq v133, 0 : i1; - v135 = cast v134 : i32; - v136 = neq v135, 0 : i1; - condbr v136, block10, block11; - -block6: - v46 = const.i32 -4 : i32; - v47 = add.wrapping v36, v46 : i32; - v48 = cast v47 : u32; - v49 = inttoptr v48 : *mut i32; - v50 = load v49 : i32; - v51 = const.i32 -8 : i32; - v52 = band v50, v51 : i32; - v53 = add.wrapping v43, v36 : i32; - v54 = const.i32 0 : i32; - v55 = sub.wrapping v54, v14 : i32; - v56 = band v53, v55 : i32; - v57 = const.i32 -8 : i32; - v58 = add.wrapping v56, v57 : i32; - v59 = const.i32 0 : i32; - v60 = sub.wrapping v58, v41 : i32; - v61 = const.i32 16 : i32; - v62 = cast v60 : u32; - v63 = cast v61 : u32; - v64 = gt v62, v63 : i1; - v65 = cast v64 : i32; - v66 = neq v65, 0 : i1; - v67 = select v66, v59, v14 : i32; - v68 = add.wrapping v58, v67 : i32; - v69 = sub.wrapping v68, v41 : i32; - v70 = sub.wrapping v52, v69 : i32; - v71 = const.i32 3 : i32; - v72 = band v50, v71 : i32; - v73 = eq v72, 0 : i1; - v74 = cast v73 : i32; - v75 = neq v74, 0 : i1; - condbr v75, block8, block9; - -block7: - br block5(v41); - -block8: - v118 = cast v41 : u32; - v119 = inttoptr v118 : *mut i32; - v120 = load v119 : i32; - v121 = cast v68 : u32; - v122 = add.checked v121, 4 : u32; - v123 = inttoptr v122 : *mut i32; - store v123, v70; - v124 = add.wrapping v120, v69 : i32; - v125 = cast v68 : u32; - v126 = inttoptr v125 : *mut i32; - store v126, v124; - br block5(v68); - -block9: - v76 = cast v68 : u32; - v77 = add.checked v76, 4 : u32; - v78 = inttoptr v77 : *mut i32; - v79 = load v78 : i32; - v80 = const.i32 1 : i32; - v81 = band v79, v80 : i32; - v82 = bor v70, v81 : i32; - v83 = const.i32 2 : i32; - v84 = bor v82, v83 : i32; - v85 = cast v68 : u32; - v86 = add.checked v85, 4 : u32; - v87 = inttoptr v86 : *mut i32; - store v87, v84; - v88 = add.wrapping v68, v70 : i32; - v89 = cast v88 : u32; - v90 = add.checked v89, 4 : u32; - v91 = inttoptr v90 : *mut i32; - v92 = load v91 : i32; - v93 = const.i32 1 : i32; - v94 = bor v92, v93 : i32; - v95 = cast v88 : u32; - v96 = add.checked v95, 4 : u32; - v97 = inttoptr v96 : *mut i32; - store v97, v94; - v98 = cast v47 : u32; - v99 = inttoptr v98 : *mut i32; - v100 = load v99 : i32; - v101 = const.i32 1 : i32; - v102 = band v100, v101 : i32; - v103 = bor v69, v102 : i32; - v104 = const.i32 2 : i32; - v105 = bor v103, v104 : i32; - v106 = cast v47 : u32; - v107 = inttoptr v106 : *mut i32; - store v107, v105; - v108 = add.wrapping v41, v69 : i32; - v109 = cast v108 : u32; - v110 = add.checked v109, 4 : u32; - v111 = inttoptr v110 : *mut i32; - v112 = load v111 : i32; - v113 = const.i32 1 : i32; - v114 = bor v112, v113 : i32; - v115 = cast v108 : u32; - v116 = add.checked v115, 4 : u32; - v117 = inttoptr v116 : *mut i32; - store v117, v114; - call noname::dlmalloc::dlmalloc::Dlmalloc::dispose_chunk(v0, v41, v69); - br block5(v68); - -block10: - v174 = const.i32 8 : i32; - v175 = add.wrapping v127, v174 : i32; - br block2(v175); - -block11: - v137 = const.i32 -8 : i32; - v138 = band v131, v137 : i32; - v140 = const.i32 16 : i32; - v141 = add.wrapping v32, v140 : i32; - v142 = cast v138 : u32; - v143 = cast v141 : u32; - v144 = lte v142, v143 : i1; - v145 = cast v144 : i32; - v146 = neq v145, 0 : i1; - condbr v146, block10, block12; - -block12: - v147 = const.i32 1 : i32; - v148 = band v131, v147 : i32; - v149 = bor v139, v148 : i32; - v150 = const.i32 2 : i32; - v151 = bor v149, v150 : i32; - v152 = cast v127 : u32; - v153 = add.checked v152, 4 : u32; - v154 = inttoptr v153 : *mut i32; - store v154, v151; - v155 = add.wrapping v127, v139 : i32; - v156 = sub.wrapping v138, v139 : i32; - v157 = const.i32 3 : i32; - v158 = bor v156, v157 : i32; - v159 = cast v155 : u32; - v160 = add.checked v159, 4 : u32; - v161 = inttoptr v160 : *mut i32; - store v161, v158; - v162 = add.wrapping v127, v138 : i32; - v163 = cast v162 : u32; - v164 = add.checked v163, 4 : u32; - v165 = inttoptr v164 : *mut i32; - v166 = load v165 : i32; - v167 = const.i32 1 : i32; - v168 = bor v166, v167 : i32; - v169 = cast v162 : u32; - v170 = add.checked v169, 4 : u32; - v171 = inttoptr v170 : *mut i32; - store v171, v168; - call noname::dlmalloc::dlmalloc::Dlmalloc::dispose_chunk(v0, v155, v156); - br block10; -} - -pub fn __main() -> i32 { -block0: - v1 = const.i32 0 : i32; - v2 = global.load (@__stack_pointer) as *mut i8 : i32; - v3 = const.i32 16 : i32; - v4 = sub.wrapping v2, v3 : i32; - v5 = global.symbol @__stack_pointer : *mut i32; - store v5, v4; - v6 = const.i32 0 : i32; - v7 = cast v4 : u32; - v8 = add.checked v7, 12 : u32; - v9 = inttoptr v8 : *mut i32; - store v9, v6; - v10 = const.i64 4 : i64; - v11 = cast v4 : u32; - v12 = add.checked v11, 4 : u32; - v13 = inttoptr v12 : *mut i64; - store v13, v10; - v14 = const.i32 4 : i32; - v15 = add.wrapping v4, v14 : i32; - v16 = const.i32 0 : i32; - call noname::alloc::raw_vec::RawVec::reserve_for_push(v15, v16); - v17 = cast v4 : u32; - v18 = add.checked v17, 4 : u32; - v19 = inttoptr v18 : *mut i32; - v20 = load v19 : i32; - v21 = cast v4 : u32; - v22 = add.checked v21, 12 : u32; - v23 = inttoptr v22 : *mut i32; - v24 = load v23 : i32; - v25 = const.i32 2 : i32; - v26 = shl.wrapping v24, v25 : i32; - v27 = add.wrapping v20, v26 : i32; - v28 = const.i32 1 : i32; - v29 = cast v27 : u32; - v30 = inttoptr v29 : *mut i32; - store v30, v28; - v31 = const.i32 -1 : i32; - v32 = eq v24, v31 : i1; - v33 = cast v32 : i32; - v34 = neq v33, 0 : i1; - condbr v34, block2, block3; - -block1(v0: i32): - -block2: - unreachable ; - -block3: - v35 = cast v4 : u32; - v36 = add.checked v35, 8 : u32; - v37 = inttoptr v36 : *mut i32; - v38 = load v37 : i32; - v39 = eq v38, 0 : i1; - v40 = cast v39 : i32; - v41 = neq v40, 0 : i1; - condbr v41, block4, block5; - -block4: - v44 = const.i32 16 : i32; - v45 = add.wrapping v4, v44 : i32; - v46 = global.symbol @__stack_pointer : *mut i32; - store v46, v45; - v47 = const.i32 1 : i32; - ret v47; - -block5: - v42 = const.i32 1048580 : i32; - call noname::dlmalloc::dlmalloc::Dlmalloc::free(v42, v20); - br block4; -} - -pub fn __rust_realloc(i32, i32, i32, i32) -> i32 { -block0(v0: i32, v1: i32, v2: i32, v3: i32): - v5 = const.i32 0 : i32; - v6 = const.i32 9 : i32; - v7 = cast v2 : u32; - v8 = cast v6 : u32; - v9 = lt v7, v8 : i1; - v10 = cast v9 : i32; - v11 = neq v10, 0 : i1; - condbr v11, block5, block6; - -block1(v4: i32): - ret v4; - -block2: - v359 = const.i32 1 : i32; - v360 = band v39, v359 : i32; - v361 = bor v34, v360 : i32; - v362 = const.i32 2 : i32; - v363 = bor v361, v362 : i32; - v364 = cast v36 : u32; - v365 = inttoptr v364 : *mut i32; - store v365, v363; - v366 = const.i32 0 : i32; - v367 = add.wrapping v48, v34 : i32; - v368 = cast v366 : u32; - v369 = add.checked v368, 1049008 : u32; - v370 = inttoptr v369 : *mut i32; - store v370, v367; - v371 = const.i32 0 : i32; - v372 = sub.wrapping v315, v34 : i32; - v373 = cast v371 : u32; - v374 = add.checked v373, 1049000 : u32; - v375 = inttoptr v374 : *mut i32; - store v375, v372; - v376 = const.i32 1 : i32; - v377 = bor v372, v376 : i32; - v378 = cast v367 : u32; - v379 = add.checked v378, 4 : u32; - v380 = inttoptr v379 : *mut i32; - store v380, v377; - br block1(v0); - -block3(v357: i32): - ret v357; - -block4: - v349 = cast v1 : u32; - v350 = cast v3 : u32; - v351 = lt v349, v350 : i1; - v352 = cast v351 : i32; - v353 = neq v352, 0 : i1; - v354 = select v353, v1, v3 : i32; - v355 = call noname::memcpy(v13, v0, v354) : i32; - v356 = const.i32 1048580 : i32; - call noname::dlmalloc::dlmalloc::Dlmalloc::free(v356, v0); - br block3(v13); - -block5: - v16 = const.i32 0 : i32; - v17 = const.i32 -65588 : i32; - v18 = cast v3 : u32; - v19 = cast v17 : u32; - v20 = gt v18, v19 : i1; - v21 = cast v20 : i32; - v22 = neq v21, 0 : i1; - condbr v22, block3(v16), block8; - -block6: - v12 = const.i32 1048580 : i32; - v13 = call noname::dlmalloc::dlmalloc::Dlmalloc::memalign(v12, v2, v3) : i32; - v14 = neq v13, 0 : i1; - condbr v14, block4, block7; - -block7: - v15 = const.i32 0 : i32; - ret v15; - -block8: - v23 = const.i32 16 : i32; - v24 = const.i32 11 : i32; - v25 = add.wrapping v3, v24 : i32; - v26 = const.i32 -8 : i32; - v27 = band v25, v26 : i32; - v28 = const.i32 11 : i32; - v29 = cast v3 : u32; - v30 = cast v28 : u32; - v31 = lt v29, v30 : i1; - v32 = cast v31 : i32; - v33 = neq v32, 0 : i1; - v34 = select v33, v23, v27 : i32; - v35 = const.i32 -4 : i32; - v36 = add.wrapping v0, v35 : i32; - v37 = cast v36 : u32; - v38 = inttoptr v37 : *mut i32; - v39 = load v38 : i32; - v40 = const.i32 -8 : i32; - v41 = band v39, v40 : i32; - v42 = const.i32 3 : i32; - v43 = band v39, v42 : i32; - v44 = eq v43, 0 : i1; - v45 = cast v44 : i32; - v46 = neq v45, 0 : i1; - condbr v46, block16, block17; - -block9: - v321 = const.i32 1048580 : i32; - v323 = call noname::dlmalloc::dlmalloc::Dlmalloc::malloc(v321, v3) : i32; - v324 = eq v323, 0 : i1; - v325 = cast v324 : i32; - v326 = neq v325, 0 : i1; - condbr v326, block3(v16), block37; - -block10: - v310 = const.i32 0 : i32; - v311 = cast v310 : u32; - v312 = add.checked v311, 1049000 : u32; - v313 = inttoptr v312 : *mut i32; - v314 = load v313 : i32; - v315 = add.wrapping v314, v41 : i32; - v316 = cast v315 : u32; - v317 = cast v34 : u32; - v318 = gt v316, v317 : i1; - v319 = cast v318 : i32; - v320 = neq v319, 0 : i1; - condbr v320, block2, block36; - -block11: - v287 = const.i32 1 : i32; - v288 = band v39, v287 : i32; - v289 = bor v34, v288 : i32; - v290 = const.i32 2 : i32; - v291 = bor v289, v290 : i32; - v292 = cast v36 : u32; - v293 = inttoptr v292 : *mut i32; - store v293, v291; - v294 = add.wrapping v48, v34 : i32; - v295 = const.i32 3 : i32; - v296 = bor v114, v295 : i32; - v297 = cast v294 : u32; - v298 = add.checked v297, 4 : u32; - v299 = inttoptr v298 : *mut i32; - store v299, v296; - v300 = cast v49 : u32; - v301 = add.checked v300, 4 : u32; - v302 = inttoptr v301 : *mut i32; - v303 = load v302 : i32; - v304 = const.i32 1 : i32; - v305 = bor v303, v304 : i32; - v306 = cast v49 : u32; - v307 = add.checked v306, 4 : u32; - v308 = inttoptr v307 : *mut i32; - store v308, v305; - v309 = const.i32 1048580 : i32; - call noname::dlmalloc::dlmalloc::Dlmalloc::dispose_chunk(v309, v294, v114); - ret v0; - -block12: - v214 = const.i32 0 : i32; - v215 = cast v214 : u32; - v216 = add.checked v215, 1048996 : u32; - v217 = inttoptr v216 : *mut i32; - v218 = load v217 : i32; - v219 = add.wrapping v218, v41 : i32; - v220 = cast v219 : u32; - v221 = cast v34 : u32; - v222 = lt v220, v221 : i1; - v223 = cast v222 : i32; - v224 = neq v223, 0 : i1; - condbr v224, block9, block32; - -block13: - v156 = const.i32 16 : i32; - v157 = cast v86 : u32; - v158 = cast v156 : u32; - v159 = lt v157, v158 : i1; - v160 = cast v159 : i32; - v161 = neq v160, 0 : i1; - condbr v161, block30, block31; - -block14: - v121 = cast v49 : u32; - v122 = add.checked v121, 12 : u32; - v123 = inttoptr v122 : *mut i32; - v124 = load v123 : i32; - v125 = cast v49 : u32; - v126 = add.checked v125, 8 : u32; - v127 = inttoptr v126 : *mut i32; - v128 = load v127 : i32; - v129 = eq v124, v128 : i1; - v130 = cast v129 : i32; - v131 = neq v130, 0 : i1; - condbr v131, block28, block29; - -block15: - v114 = sub.wrapping v41, v34 : i32; - v115 = const.i32 16 : i32; - v116 = cast v114 : u32; - v117 = cast v115 : u32; - v118 = gte v116, v117 : i1; - v119 = cast v118 : i32; - v120 = neq v119, 0 : i1; - condbr v120, block11, block27; - -block16: - v94 = const.i32 256 : i32; - v95 = cast v34 : u32; - v96 = cast v94 : u32; - v97 = lt v95, v96 : i1; - v98 = cast v97 : i32; - v99 = neq v98, 0 : i1; - condbr v99, block9, block24; - -block17: - v47 = const.i32 -8 : i32; - v48 = add.wrapping v0, v47 : i32; - v49 = add.wrapping v48, v41 : i32; - v50 = cast v41 : u32; - v51 = cast v34 : u32; - v52 = gte v50, v51 : i1; - v53 = cast v52 : i32; - v54 = neq v53, 0 : i1; - condbr v54, block15, block18; - -block18: - v55 = const.i32 0 : i32; - v56 = cast v55 : u32; - v57 = add.checked v56, 1049008 : u32; - v58 = inttoptr v57 : *mut i32; - v59 = load v58 : i32; - v60 = eq v49, v59 : i1; - v61 = cast v60 : i32; - v62 = neq v61, 0 : i1; - condbr v62, block10, block19; - -block19: - v63 = const.i32 0 : i32; - v64 = cast v63 : u32; - v65 = add.checked v64, 1049004 : u32; - v66 = inttoptr v65 : *mut i32; - v67 = load v66 : i32; - v68 = eq v49, v67 : i1; - v69 = cast v68 : i32; - v70 = neq v69, 0 : i1; - condbr v70, block12, block20; - -block20: - v71 = cast v49 : u32; - v72 = add.checked v71, 4 : u32; - v73 = inttoptr v72 : *mut i32; - v74 = load v73 : i32; - v75 = const.i32 2 : i32; - v76 = band v74, v75 : i32; - v77 = neq v76, 0 : i1; - condbr v77, block9, block21; - -block21: - v78 = const.i32 -8 : i32; - v79 = band v74, v78 : i32; - v80 = add.wrapping v79, v41 : i32; - v81 = cast v80 : u32; - v82 = cast v34 : u32; - v83 = lt v81, v82 : i1; - v84 = cast v83 : i32; - v85 = neq v84, 0 : i1; - condbr v85, block9, block22; - -block22: - v86 = sub.wrapping v80, v34 : i32; - v87 = const.i32 256 : i32; - v88 = cast v79 : u32; - v89 = cast v87 : u32; - v90 = lt v88, v89 : i1; - v91 = cast v90 : i32; - v92 = neq v91, 0 : i1; - condbr v92, block14, block23; - -block23: - v93 = const.i32 1048580 : i32; - call noname::dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk(v93, v49); - br block13; - -block24: - v100 = const.i32 4 : i32; - v101 = bor v34, v100 : i32; - v102 = cast v41 : u32; - v103 = cast v101 : u32; - v104 = lt v102, v103 : i1; - v105 = cast v104 : i32; - v106 = neq v105, 0 : i1; - condbr v106, block9, block25; - -block25: - v107 = sub.wrapping v41, v34 : i32; - v108 = const.i32 131073 : i32; - v109 = cast v107 : u32; - v110 = cast v108 : u32; - v111 = gte v109, v110 : i1; - v112 = cast v111 : i32; - v113 = neq v112, 0 : i1; - condbr v113, block9, block26; - -block26: - ret v0; - -block27: - ret v0; - -block28: - v138 = const.i32 0 : i32; - v139 = const.i32 0 : i32; - v140 = cast v139 : u32; - v141 = add.checked v140, 1048988 : u32; - v142 = inttoptr v141 : *mut i32; - v143 = load v142 : i32; - v144 = const.i32 -2 : i32; - v145 = const.i32 3 : i32; - v146 = cast v74 : u32; - v147 = cast v145 : u32; - v148 = shr.wrapping v146, v147 : u32; - v149 = cast v148 : i32; - v150 = rotl v144, v149 : i32; - v151 = band v143, v150 : i32; - v152 = cast v138 : u32; - v153 = add.checked v152, 1048988 : u32; - v154 = inttoptr v153 : *mut i32; - store v154, v151; - br block13; - -block29: - v132 = cast v128 : u32; - v133 = add.checked v132, 12 : u32; - v134 = inttoptr v133 : *mut i32; - store v134, v124; - v135 = cast v124 : u32; - v136 = add.checked v135, 8 : u32; - v137 = inttoptr v136 : *mut i32; - store v137, v128; - br block13; - -block30: - v194 = cast v162 : u32; - v195 = inttoptr v194 : *mut i32; - v196 = load v195 : i32; - v197 = const.i32 1 : i32; - v198 = band v196, v197 : i32; - v199 = bor v181, v198 : i32; - v200 = const.i32 2 : i32; - v201 = bor v199, v200 : i32; - v202 = cast v162 : u32; - v203 = inttoptr v202 : *mut i32; - store v203, v201; - v204 = add.wrapping v174, v181 : i32; - v205 = cast v204 : u32; - v206 = add.checked v205, 4 : u32; - v207 = inttoptr v206 : *mut i32; - v208 = load v207 : i32; - v209 = const.i32 1 : i32; - v210 = bor v208, v209 : i32; - v211 = cast v204 : u32; - v212 = add.checked v211, 4 : u32; - v213 = inttoptr v212 : *mut i32; - store v213, v210; - ret v193; - -block31: - v164 = cast v162 : u32; - v165 = inttoptr v164 : *mut i32; - v166 = load v165 : i32; - v167 = const.i32 1 : i32; - v168 = band v166, v167 : i32; - v169 = bor v34, v168 : i32; - v170 = const.i32 2 : i32; - v171 = bor v169, v170 : i32; - v172 = cast v36 : u32; - v173 = inttoptr v172 : *mut i32; - store v173, v171; - v175 = add.wrapping v48, v163 : i32; - v176 = const.i32 3 : i32; - v177 = bor v155, v176 : i32; - v178 = cast v175 : u32; - v179 = add.checked v178, 4 : u32; - v180 = inttoptr v179 : *mut i32; - store v180, v177; - v182 = add.wrapping v174, v80 : i32; - v183 = cast v182 : u32; - v184 = add.checked v183, 4 : u32; - v185 = inttoptr v184 : *mut i32; - v186 = load v185 : i32; - v187 = const.i32 1 : i32; - v188 = bor v186, v187 : i32; - v189 = cast v182 : u32; - v190 = add.checked v189, 4 : u32; - v191 = inttoptr v190 : *mut i32; - store v191, v188; - v192 = const.i32 1048580 : i32; - call noname::dlmalloc::dlmalloc::Dlmalloc::dispose_chunk(v192, v175, v155); - ret v0; - -block32: - v225 = sub.wrapping v219, v34 : i32; - v226 = const.i32 15 : i32; - v227 = cast v225 : u32; - v228 = cast v226 : u32; - v229 = gt v227, v228 : i1; - v230 = cast v229 : i32; - v231 = neq v230, 0 : i1; - condbr v231, block34, block35; - -block33(v277: i32, v282: i32): - v276 = const.i32 0 : i32; - v278 = cast v276 : u32; - v279 = add.checked v278, 1049004 : u32; - v280 = inttoptr v279 : *mut i32; - store v280, v277; - v281 = const.i32 0 : i32; - v283 = cast v281 : u32; - v284 = add.checked v283, 1048996 : u32; - v285 = inttoptr v284 : *mut i32; - store v285, v282; - ret v0; - -block34: - v251 = const.i32 1 : i32; - v252 = band v39, v251 : i32; - v253 = bor v34, v252 : i32; - v254 = const.i32 2 : i32; - v255 = bor v253, v254 : i32; - v256 = cast v36 : u32; - v257 = inttoptr v256 : *mut i32; - store v257, v255; - v258 = add.wrapping v48, v34 : i32; - v259 = const.i32 1 : i32; - v260 = bor v225, v259 : i32; - v261 = cast v258 : u32; - v262 = add.checked v261, 4 : u32; - v263 = inttoptr v262 : *mut i32; - store v263, v260; - v264 = add.wrapping v48, v219 : i32; - v265 = cast v264 : u32; - v266 = inttoptr v265 : *mut i32; - store v266, v225; - v267 = cast v264 : u32; - v268 = add.checked v267, 4 : u32; - v269 = inttoptr v268 : *mut i32; - v270 = load v269 : i32; - v271 = const.i32 -2 : i32; - v272 = band v270, v271 : i32; - v273 = cast v264 : u32; - v274 = add.checked v273, 4 : u32; - v275 = inttoptr v274 : *mut i32; - store v275, v272; - br block33(v258, v225); - -block35: - v232 = const.i32 1 : i32; - v233 = band v39, v232 : i32; - v234 = bor v233, v219 : i32; - v235 = const.i32 2 : i32; - v236 = bor v234, v235 : i32; - v237 = cast v36 : u32; - v238 = inttoptr v237 : *mut i32; - store v238, v236; - v239 = add.wrapping v48, v219 : i32; - v240 = cast v239 : u32; - v241 = add.checked v240, 4 : u32; - v242 = inttoptr v241 : *mut i32; - v243 = load v242 : i32; - v244 = const.i32 1 : i32; - v245 = bor v243, v244 : i32; - v246 = cast v239 : u32; - v247 = add.checked v246, 4 : u32; - v248 = inttoptr v247 : *mut i32; - store v248, v245; - v249 = const.i32 0 : i32; - v250 = const.i32 0 : i32; - br block33(v250, v249); - -block36: - br block9; - -block37: - v328 = const.i32 -4 : i32; - v329 = const.i32 -8 : i32; - v331 = cast v36 : u32; - v332 = inttoptr v331 : *mut i32; - v333 = load v332 : i32; - v334 = const.i32 3 : i32; - v335 = band v333, v334 : i32; - v336 = neq v335, 0 : i1; - v337 = select v336, v328, v329 : i32; - v338 = const.i32 -8 : i32; - v339 = band v333, v338 : i32; - v340 = add.wrapping v337, v339 : i32; - v341 = cast v340 : u32; - v342 = cast v322 : u32; - v343 = lt v341, v342 : i1; - v344 = cast v343 : i32; - v345 = neq v344, 0 : i1; - v346 = select v345, v340, v322 : i32; - v347 = call noname::memcpy(v323, v0, v346) : i32; - v348 = const.i32 1048580 : i32; - call noname::dlmalloc::dlmalloc::Dlmalloc::free(v348, v327); - ret v347; -} - -pub fn alloc::raw_vec::finish_grow(i32, i32, i32, i32) { -block0(v0: i32, v1: i32, v2: i32, v3: i32): - v4 = const.i32 0 : i32; - v5 = eq v1, 0 : i1; - v6 = cast v5 : i32; - v7 = neq v6, 0 : i1; - condbr v7, block4, block5; - -block1: - ret; - -block2(v99: i32): - v100 = const.i32 1 : i32; - v101 = cast v99 : u32; - v102 = inttoptr v101 : *mut i32; - store v102, v100; - br block1; - -block3: - v95 = const.i32 0 : i32; - v96 = cast v0 : u32; - v97 = add.checked v96, 4 : u32; - v98 = inttoptr v97 : *mut i32; - store v98, v95; - br block2(v0); - -block4: - v87 = const.i32 0 : i32; - v88 = cast v0 : u32; - v89 = add.checked v88, 4 : u32; - v90 = inttoptr v89 : *mut i32; - store v90, v87; - v91 = const.i32 8 : i32; - v92 = add.wrapping v0, v91 : i32; - v93 = cast v92 : u32; - v94 = inttoptr v93 : *mut i32; - store v94, v2; - br block2(v0); - -block5: - v8 = const.i32 -1 : i32; - v9 = lte v2, v8 : i1; - v10 = cast v9 : i32; - v11 = neq v10, 0 : i1; - condbr v11, block3, block6; - -block6: - v12 = cast v3 : u32; - v13 = add.checked v12, 4 : u32; - v14 = inttoptr v13 : *mut i32; - v15 = load v14 : i32; - v16 = eq v15, 0 : i1; - v17 = cast v16 : i32; - v18 = neq v17, 0 : i1; - condbr v18, block8, block9; - -block7(v63: i32): - v64 = eq v63, 0 : i1; - v65 = cast v64 : i32; - v66 = neq v65, 0 : i1; - condbr v66, block20, block21; - -block8: - v46 = neq v2, 0 : i1; - condbr v46, block16, block17; - -block9: - v19 = const.i32 8 : i32; - v20 = add.wrapping v3, v19 : i32; - v21 = cast v20 : u32; - v22 = inttoptr v21 : *mut i32; - v23 = load v22 : i32; - v24 = neq v23, 0 : i1; - condbr v24, block10, block11; - -block10: - v42 = cast v3 : u32; - v43 = inttoptr v42 : *mut i32; - v44 = load v43 : i32; - v45 = call noname::__rust_realloc(v44, v23, v1, v2) : i32; - br block7(v45); - -block11: - v25 = neq v2, 0 : i1; - condbr v25, block12, block13; - -block12: - v26 = const.i32 0 : i32; - v27 = cast v26 : u32; - v28 = add.checked v27, 1048576 : u32; - v29 = inttoptr v28 : *mut u8; - v30 = load v29 : u8; - v31 = zext v30 : i32; - v32 = const.i32 9 : i32; - v33 = cast v1 : u32; - v34 = cast v32 : u32; - v35 = lt v33, v34 : i1; - v36 = cast v35 : i32; - v37 = neq v36, 0 : i1; - condbr v37, block14, block15; - -block13: - br block7(v1); - -block14: - v40 = const.i32 1048580 : i32; - v41 = call noname::dlmalloc::dlmalloc::Dlmalloc::malloc(v40, v2) : i32; - br block7(v41); - -block15: - v38 = const.i32 1048580 : i32; - v39 = call noname::dlmalloc::dlmalloc::Dlmalloc::memalign(v38, v1, v2) : i32; - br block7(v39); - -block16: - v47 = const.i32 0 : i32; - v48 = cast v47 : u32; - v49 = add.checked v48, 1048576 : u32; - v50 = inttoptr v49 : *mut u8; - v51 = load v50 : u8; - v52 = zext v51 : i32; - v53 = const.i32 9 : i32; - v54 = cast v1 : u32; - v55 = cast v53 : u32; - v56 = lt v54, v55 : i1; - v57 = cast v56 : i32; - v58 = neq v57, 0 : i1; - condbr v58, block18, block19; - -block17: - br block7(v1); - -block18: - v61 = const.i32 1048580 : i32; - v62 = call noname::dlmalloc::dlmalloc::Dlmalloc::malloc(v61, v2) : i32; - br block7(v62); - -block19: - v59 = const.i32 1048580 : i32; - v60 = call noname::dlmalloc::dlmalloc::Dlmalloc::memalign(v59, v1, v2) : i32; - br block7(v60); - -block20: - v80 = cast v67 : u32; - v81 = add.checked v80, 4 : u32; - v82 = inttoptr v81 : *mut i32; - store v82, v1; - v83 = const.i32 8 : i32; - v84 = add.wrapping v67, v83 : i32; - v85 = cast v84 : u32; - v86 = inttoptr v85 : *mut i32; - store v86, v73; - br block2(v67); - -block21: - v68 = cast v0 : u32; - v69 = add.checked v68, 4 : u32; - v70 = inttoptr v69 : *mut i32; - store v70, v63; - v71 = const.i32 8 : i32; - v72 = add.wrapping v67, v71 : i32; - v74 = cast v72 : u32; - v75 = inttoptr v74 : *mut i32; - store v75, v2; - v76 = const.i32 0 : i32; - v77 = cast v67 : u32; - v78 = inttoptr v77 : *mut i32; - store v78, v76; - ret; -} - -pub fn alloc::raw_vec::RawVec::reserve_for_push(i32, i32) { -block0(v0: i32, v1: i32): - v2 = const.i32 0 : i32; - v3 = global.load (@__stack_pointer) as *mut i8 : i32; - v4 = const.i32 32 : i32; - v5 = sub.wrapping v3, v4 : i32; - v6 = global.symbol @__stack_pointer : *mut i32; - store v6, v5; - v7 = const.i32 1 : i32; - v8 = add.wrapping v1, v7 : i32; - v9 = eq v8, 0 : i1; - v10 = cast v9 : i32; - v11 = neq v10, 0 : i1; - condbr v11, block3, block4; - -block1: - ret; - -block2: - v89 = const.i32 32 : i32; - v90 = add.wrapping v61, v89 : i32; - v91 = global.symbol @__stack_pointer : *mut i32; - store v91, v90; - br block1; - -block3: - unreachable ; - -block4: - v12 = cast v0 : u32; - v13 = add.checked v12, 4 : u32; - v14 = inttoptr v13 : *mut i32; - v15 = load v14 : i32; - v16 = const.i32 1 : i32; - v17 = shl.wrapping v15, v16 : i32; - v18 = cast v17 : u32; - v19 = cast v8 : u32; - v20 = gt v18, v19 : i1; - v21 = cast v20 : i32; - v22 = neq v21, 0 : i1; - v23 = select v22, v17, v8 : i32; - v24 = const.i32 4 : i32; - v25 = const.i32 4 : i32; - v26 = cast v23 : u32; - v27 = cast v25 : u32; - v28 = gt v26, v27 : i1; - v29 = cast v28 : i32; - v30 = neq v29, 0 : i1; - v31 = select v30, v23, v24 : i32; - v32 = const.i32 2 : i32; - v33 = shl.wrapping v31, v32 : i32; - v34 = const.i32 536870912 : i32; - v35 = cast v31 : u32; - v36 = cast v34 : u32; - v37 = lt v35, v36 : i1; - v38 = cast v37 : i32; - v39 = const.i32 2 : i32; - v40 = shl.wrapping v38, v39 : i32; - v41 = neq v15, 0 : i1; - condbr v41, block6, block7; - -block5: - v62 = const.i32 8 : i32; - v63 = add.wrapping v5, v62 : i32; - v66 = const.i32 20 : i32; - v67 = add.wrapping v61, v66 : i32; - call noname::alloc::raw_vec::finish_grow(v63, v40, v33, v67); - v68 = cast v61 : u32; - v69 = add.checked v68, 12 : u32; - v70 = inttoptr v69 : *mut i32; - v71 = load v70 : i32; - v72 = cast v61 : u32; - v73 = add.checked v72, 8 : u32; - v74 = inttoptr v73 : *mut i32; - v75 = load v74 : i32; - v76 = neq v75, 0 : i1; - condbr v76, block8, block9; - -block6: - v46 = const.i32 4 : i32; - v47 = cast v5 : u32; - v48 = add.checked v47, 24 : u32; - v49 = inttoptr v48 : *mut i32; - store v49, v46; - v50 = const.i32 2 : i32; - v51 = shl.wrapping v15, v50 : i32; - v52 = cast v5 : u32; - v53 = add.checked v52, 28 : u32; - v54 = inttoptr v53 : *mut i32; - store v54, v51; - v55 = cast v0 : u32; - v56 = inttoptr v55 : *mut i32; - v57 = load v56 : i32; - v58 = cast v5 : u32; - v59 = add.checked v58, 20 : u32; - v60 = inttoptr v59 : *mut i32; - store v60, v57; - br block5; - -block7: - v42 = const.i32 0 : i32; - v43 = cast v5 : u32; - v44 = add.checked v43, 24 : u32; - v45 = inttoptr v44 : *mut i32; - store v45, v42; - br block5; - -block8: - v84 = const.i32 -2147483647 : i32; - v85 = eq v71, v84 : i1; - v86 = cast v85 : i32; - v87 = neq v86, 0 : i1; - condbr v87, block2, block10; - -block9: - v79 = cast v0 : u32; - v80 = add.checked v79, 4 : u32; - v81 = inttoptr v80 : *mut i32; - store v81, v31; - v82 = cast v77 : u32; - v83 = inttoptr v82 : *mut i32; - store v83, v71; - br block2; - -block10: - br block3; -} - -pub fn vec_alloc() -> i32 { -block0: - v1 = const.i32 0 : i32; - v2 = global.load (@__stack_pointer) as *mut i8 : i32; - v3 = const.i32 16 : i32; - v4 = sub.wrapping v2, v3 : i32; - v5 = global.symbol @__stack_pointer : *mut i32; - store v5, v4; - v6 = const.i32 0 : i32; - v7 = cast v4 : u32; - v8 = add.checked v7, 12 : u32; - v9 = inttoptr v8 : *mut i32; - store v9, v6; - v10 = const.i64 4 : i64; - v11 = cast v4 : u32; - v12 = add.checked v11, 4 : u32; - v13 = inttoptr v12 : *mut i64; - store v13, v10; - v14 = const.i32 4 : i32; - v15 = add.wrapping v4, v14 : i32; - v16 = const.i32 0 : i32; - call noname::alloc::raw_vec::RawVec::reserve_for_push(v15, v16); - v17 = cast v4 : u32; - v18 = add.checked v17, 4 : u32; - v19 = inttoptr v18 : *mut i32; - v20 = load v19 : i32; - v21 = cast v4 : u32; - v22 = add.checked v21, 12 : u32; - v23 = inttoptr v22 : *mut i32; - v24 = load v23 : i32; - v25 = const.i32 2 : i32; - v26 = shl.wrapping v24, v25 : i32; - v27 = add.wrapping v20, v26 : i32; - v28 = const.i32 1 : i32; - v29 = cast v27 : u32; - v30 = inttoptr v29 : *mut i32; - store v30, v28; - v31 = const.i32 -1 : i32; - v32 = eq v24, v31 : i1; - v33 = cast v32 : i32; - v34 = neq v33, 0 : i1; - condbr v34, block2, block3; - -block1(v0: i32): - -block2: - unreachable ; - -block3: - v35 = cast v4 : u32; - v36 = add.checked v35, 8 : u32; - v37 = inttoptr v36 : *mut i32; - v38 = load v37 : i32; - v39 = eq v38, 0 : i1; - v40 = cast v39 : i32; - v41 = neq v40, 0 : i1; - condbr v41, block4, block5; - -block4: - v44 = const.i32 16 : i32; - v45 = add.wrapping v4, v44 : i32; - v46 = global.symbol @__stack_pointer : *mut i32; - store v46, v45; - v47 = const.i32 1 : i32; - ret v47; - -block5: - v42 = const.i32 1048580 : i32; - call noname::dlmalloc::dlmalloc::Dlmalloc::free(v42, v20); - br block4; -} - -pub fn compiler_builtins::mem::memcpy(i32, i32, i32) -> i32 { -block0(v0: i32, v1: i32, v2: i32): - v4 = const.i32 0 : i32; - v5 = const.i32 16 : i32; - v6 = cast v2 : u32; - v7 = cast v5 : u32; - v8 = gte v6, v7 : i1; - v9 = cast v8 : i32; - v10 = neq v9, 0 : i1; - condbr v10, block3, block4; - -block1(v3: i32): - ret v3; - -block2(v133: i32, v137: i32, v159: i32, v161: i32): - v134 = eq v133, 0 : i1; - v135 = cast v134 : i32; - v136 = neq v135, 0 : i1; - condbr v136, block21, block22; - -block3: - v11 = const.i32 0 : i32; - v12 = sub.wrapping v11, v0 : i32; - v13 = const.i32 3 : i32; - v14 = band v12, v13 : i32; - v15 = add.wrapping v0, v14 : i32; - v16 = eq v14, 0 : i1; - v17 = cast v16 : i32; - v18 = neq v17, 0 : i1; - condbr v18, block5(v15), block6; - -block4: - br block2(v2, v0, v1, v0); - -block5(v38: i32): - v43 = sub.wrapping v2, v14 : i32; - v44 = const.i32 -4 : i32; - v45 = band v43, v44 : i32; - v46 = add.wrapping v38, v45 : i32; - v49 = add.wrapping v1, v41 : i32; - v50 = const.i32 3 : i32; - v51 = band v49, v50 : i32; - v52 = eq v51, 0 : i1; - v53 = cast v52 : i32; - v54 = neq v53, 0 : i1; - condbr v54, block11, block12; - -block6: - br block7(v0, v1); - -block7(v19: i32, v20: i32): - v21 = cast v20 : u32; - v22 = inttoptr v21 : *mut u8; - v23 = load v22 : u8; - v24 = zext v23 : i32; - v25 = trunc v24 : u8; - v26 = cast v19 : u32; - v27 = inttoptr v26 : *mut u8; - store v27, v25; - v28 = const.i32 1 : i32; - v29 = add.wrapping v20, v28 : i32; - v30 = const.i32 1 : i32; - v31 = add.wrapping v19, v30 : i32; - v33 = cast v31 : u32; - v34 = cast v32 : u32; - v35 = lt v33, v34 : i1; - v36 = cast v35 : i32; - v37 = neq v36, 0 : i1; - condbr v37, block7(v31, v29), block9; - -block8: - br block5(v32); - -block9: - br block8; - -block10(v138: i32, v162: i32): - v124 = const.i32 3 : i32; - v125 = band v43, v124 : i32; - v132 = add.wrapping v49, v45 : i32; - br block2(v125, v138, v132, v162); - -block11: - v100 = const.i32 1 : i32; - v101 = lt v45, v100 : i1; - v102 = cast v101 : i32; - v103 = neq v102, 0 : i1; - condbr v103, block10(v46, v163), block17; - -block12: - v55 = const.i32 1 : i32; - v56 = lt v45, v55 : i1; - v57 = cast v56 : i32; - v58 = neq v57, 0 : i1; - condbr v58, block10(v46, v0), block13; - -block13: - v59 = const.i32 3 : i32; - v60 = shl.wrapping v49, v59 : i32; - v61 = const.i32 24 : i32; - v62 = band v60, v61 : i32; - v63 = const.i32 -4 : i32; - v64 = band v49, v63 : i32; - v65 = const.i32 4 : i32; - v66 = add.wrapping v64, v65 : i32; - v67 = const.i32 0 : i32; - v68 = sub.wrapping v67, v60 : i32; - v69 = const.i32 24 : i32; - v70 = band v68, v69 : i32; - v71 = cast v64 : u32; - v72 = inttoptr v71 : *mut i32; - v73 = load v72 : i32; - br block14(v38, v73, v66); - -block14(v74: i32, v75: i32, v81: i32): - v77 = cast v75 : u32; - v78 = cast v76 : u32; - v79 = shr.wrapping v77, v78 : u32; - v80 = cast v79 : i32; - v82 = cast v81 : u32; - v83 = inttoptr v82 : *mut i32; - v84 = load v83 : i32; - v86 = shl.wrapping v84, v85 : i32; - v87 = bor v80, v86 : i32; - v88 = cast v74 : u32; - v89 = inttoptr v88 : *mut i32; - store v89, v87; - v90 = const.i32 4 : i32; - v91 = add.wrapping v81, v90 : i32; - v92 = const.i32 4 : i32; - v93 = add.wrapping v74, v92 : i32; - v95 = cast v93 : u32; - v96 = cast v94 : u32; - v97 = lt v95, v96 : i1; - v98 = cast v97 : i32; - v99 = neq v98, 0 : i1; - condbr v99, block14(v93, v84, v91), block16; - -block15: - -block16: - br block10(v94, v163); - -block17: - br block18(v38, v49); - -block18(v104: i32, v105: i32): - v106 = cast v105 : u32; - v107 = inttoptr v106 : *mut i32; - v108 = load v107 : i32; - v109 = cast v104 : u32; - v110 = inttoptr v109 : *mut i32; - store v110, v108; - v111 = const.i32 4 : i32; - v112 = add.wrapping v105, v111 : i32; - v113 = const.i32 4 : i32; - v114 = add.wrapping v104, v113 : i32; - v116 = cast v114 : u32; - v117 = cast v115 : u32; - v118 = lt v116, v117 : i1; - v119 = cast v118 : i32; - v120 = neq v119, 0 : i1; - condbr v120, block18(v114, v112), block20; - -block19: - br block10(v115, v163); - -block20: - br block19; - -block21: - br block1(v161); - -block22: - v139 = add.wrapping v137, v133 : i32; - br block23(v137, v159); - -block23(v140: i32, v141: i32): - v142 = cast v141 : u32; - v143 = inttoptr v142 : *mut u8; - v144 = load v143 : u8; - v145 = zext v144 : i32; - v146 = trunc v145 : u8; - v147 = cast v140 : u32; - v148 = inttoptr v147 : *mut u8; - store v148, v146; - v149 = const.i32 1 : i32; - v150 = add.wrapping v141, v149 : i32; - v151 = const.i32 1 : i32; - v152 = add.wrapping v140, v151 : i32; - v154 = cast v152 : u32; - v155 = cast v153 : u32; - v156 = lt v154, v155 : i1; - v157 = cast v156 : i32; - v158 = neq v157, 0 : i1; - condbr v158, block23(v152, v150), block25; - -block24: - br block21; - -block25: - br block24; -} - -pub fn "memcpy"(i32, i32, i32) -> i32 { -block0(v0: i32, v1: i32, v2: i32): - v4 = call noname::compiler_builtins::mem::memcpy(v0, v1, v2) : i32; - br block1(v4); - -block1(v3: i32): - ret v3; -} diff --git a/frontend-wasm/tests/expected/dlmalloc.wat b/frontend-wasm/tests/expected/dlmalloc.wat deleted file mode 100644 index 2d6c8e05b..000000000 --- a/frontend-wasm/tests/expected/dlmalloc.wat +++ /dev/null @@ -1,3855 +0,0 @@ -(module - (type (;0;) (func (param i32 i32 i32))) - (type (;1;) (func (param i32 i32))) - (type (;2;) (func (param i32 i32) (result i32))) - (type (;3;) (func (param i32 i32 i32) (result i32))) - (type (;4;) (func (result i32))) - (type (;5;) (func (param i32 i32 i32 i32) (result i32))) - (type (;6;) (func (param i32 i32 i32 i32))) - (func $dlmalloc::dlmalloc::Dlmalloc::dispose_chunk (;0;) (type 0) (param i32 i32 i32) - (local i32 i32 i32 i32) - local.get 1 - local.get 2 - i32.add - local.set 3 - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 1 - i32.load offset=4 - local.tee 4 - i32.const 1 - i32.and - br_if 0 (;@3;) - local.get 4 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@2;) - local.get 1 - i32.load - local.tee 4 - local.get 2 - i32.add - local.set 2 - block ;; label = @4 - local.get 1 - local.get 4 - i32.sub - local.tee 1 - local.get 0 - i32.load offset=424 - i32.ne - br_if 0 (;@4;) - local.get 3 - i32.load offset=4 - local.tee 4 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 1 (;@3;) - local.get 3 - local.get 4 - i32.const -2 - i32.and - i32.store offset=4 - local.get 1 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 2 - i32.store offset=416 - local.get 3 - local.get 2 - i32.store - return - end - block ;; label = @4 - local.get 4 - i32.const 256 - i32.lt_u - br_if 0 (;@4;) - local.get 0 - local.get 1 - call $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk - br 1 (;@3;) - end - block ;; label = @4 - local.get 1 - i32.load offset=12 - local.tee 5 - local.get 1 - i32.load offset=8 - local.tee 6 - i32.eq - br_if 0 (;@4;) - local.get 6 - local.get 5 - i32.store offset=12 - local.get 5 - local.get 6 - i32.store offset=8 - br 1 (;@3;) - end - local.get 0 - local.get 0 - i32.load offset=408 - i32.const -2 - local.get 4 - i32.const 3 - i32.shr_u - i32.rotl - i32.and - i32.store offset=408 - end - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - local.get 3 - i32.load offset=4 - local.tee 4 - i32.const 2 - i32.and - br_if 0 (;@5;) - local.get 3 - local.get 0 - i32.load offset=428 - i32.eq - br_if 2 (;@3;) - local.get 3 - local.get 0 - i32.load offset=424 - i32.eq - br_if 4 (;@1;) - local.get 4 - i32.const -8 - i32.and - local.tee 5 - local.get 2 - i32.add - local.set 2 - block ;; label = @6 - block ;; label = @7 - local.get 5 - i32.const 256 - i32.lt_u - br_if 0 (;@7;) - local.get 0 - local.get 3 - call $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk - br 1 (;@6;) - end - block ;; label = @7 - local.get 3 - i32.load offset=12 - local.tee 5 - local.get 3 - i32.load offset=8 - local.tee 3 - i32.eq - br_if 0 (;@7;) - local.get 3 - local.get 5 - i32.store offset=12 - local.get 5 - local.get 3 - i32.store offset=8 - br 1 (;@6;) - end - local.get 0 - local.get 0 - i32.load offset=408 - i32.const -2 - local.get 4 - i32.const 3 - i32.shr_u - i32.rotl - i32.and - i32.store offset=408 - end - local.get 1 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 2 - i32.add - local.get 2 - i32.store - local.get 1 - local.get 0 - i32.load offset=424 - i32.ne - br_if 1 (;@4;) - local.get 0 - local.get 2 - i32.store offset=416 - return - end - local.get 3 - local.get 4 - i32.const -2 - i32.and - i32.store offset=4 - local.get 1 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 2 - i32.add - local.get 2 - i32.store - end - block ;; label = @4 - local.get 2 - i32.const 256 - i32.lt_u - br_if 0 (;@4;) - local.get 0 - local.get 1 - local.get 2 - call $dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk - return - end - local.get 0 - local.get 2 - i32.const -8 - i32.and - i32.add - i32.const 144 - i32.add - local.set 3 - block ;; label = @4 - block ;; label = @5 - local.get 0 - i32.load offset=408 - local.tee 4 - i32.const 1 - local.get 2 - i32.const 3 - i32.shr_u - i32.shl - local.tee 2 - i32.and - br_if 0 (;@5;) - local.get 0 - local.get 4 - local.get 2 - i32.or - i32.store offset=408 - local.get 3 - local.set 2 - br 1 (;@4;) - end - local.get 3 - i32.load offset=8 - local.set 2 - end - local.get 3 - local.get 1 - i32.store offset=8 - local.get 2 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 3 - i32.store offset=12 - local.get 1 - local.get 2 - i32.store offset=8 - return - end - local.get 0 - local.get 1 - i32.store offset=428 - local.get 0 - local.get 0 - i32.load offset=420 - local.get 2 - i32.add - local.tee 2 - i32.store offset=420 - local.get 1 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 0 - i32.load offset=424 - i32.ne - br_if 0 (;@2;) - local.get 0 - i32.const 0 - i32.store offset=416 - local.get 0 - i32.const 0 - i32.store offset=424 - end - return - end - local.get 1 - local.get 0 - i32.load offset=416 - local.get 2 - i32.add - local.tee 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.store offset=424 - local.get 0 - local.get 2 - i32.store offset=416 - local.get 1 - local.get 2 - i32.add - local.get 2 - i32.store - ) - (func $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk (;1;) (type 1) (param i32 i32) - (local i32 i32 i32 i32 i32) - local.get 1 - i32.load offset=24 - local.set 2 - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 1 - i32.load offset=12 - local.tee 3 - local.get 1 - i32.ne - br_if 0 (;@3;) - local.get 1 - i32.const 20 - i32.const 16 - local.get 1 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - select - i32.add - i32.load - local.tee 5 - br_if 1 (;@2;) - i32.const 0 - local.set 3 - br 2 (;@1;) - end - local.get 1 - i32.load offset=8 - local.tee 5 - local.get 3 - i32.store offset=12 - local.get 3 - local.get 5 - i32.store offset=8 - br 1 (;@1;) - end - local.get 3 - local.get 1 - i32.const 16 - i32.add - local.get 4 - select - local.set 4 - loop ;; label = @2 - local.get 4 - local.set 6 - local.get 5 - local.tee 3 - i32.const 20 - i32.add - local.tee 5 - local.get 3 - i32.const 16 - i32.add - local.get 5 - i32.load - local.tee 5 - select - local.set 4 - local.get 3 - i32.const 20 - i32.const 16 - local.get 5 - select - i32.add - i32.load - local.tee 5 - br_if 0 (;@2;) - end - local.get 6 - i32.const 0 - i32.store - end - block ;; label = @1 - local.get 2 - i32.eqz - br_if 0 (;@1;) - block ;; label = @2 - block ;; label = @3 - local.get 0 - local.get 1 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.add - local.tee 5 - i32.load - local.get 1 - i32.eq - br_if 0 (;@3;) - local.get 2 - i32.const 16 - i32.const 20 - local.get 2 - i32.load offset=16 - local.get 1 - i32.eq - select - i32.add - local.get 3 - i32.store - local.get 3 - br_if 1 (;@2;) - br 2 (;@1;) - end - local.get 5 - local.get 3 - i32.store - local.get 3 - br_if 0 (;@2;) - local.get 0 - local.get 0 - i32.load offset=412 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=412 - return - end - local.get 3 - local.get 2 - i32.store offset=24 - block ;; label = @2 - local.get 1 - i32.load offset=16 - local.tee 5 - i32.eqz - br_if 0 (;@2;) - local.get 3 - local.get 5 - i32.store offset=16 - local.get 5 - local.get 3 - i32.store offset=24 - end - local.get 1 - i32.const 20 - i32.add - i32.load - local.tee 5 - i32.eqz - br_if 0 (;@1;) - local.get 3 - i32.const 20 - i32.add - local.get 5 - i32.store - local.get 5 - local.get 3 - i32.store offset=24 - return - end - ) - (func $dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk (;2;) (type 0) (param i32 i32 i32) - (local i32 i32 i32 i32) - i32.const 0 - local.set 3 - block ;; label = @1 - local.get 2 - i32.const 256 - i32.lt_u - br_if 0 (;@1;) - i32.const 31 - local.set 3 - local.get 2 - i32.const 16777215 - i32.gt_u - br_if 0 (;@1;) - local.get 2 - i32.const 6 - local.get 2 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 1 - i64.const 0 - i64.store offset=16 align=4 - local.get 1 - local.get 3 - i32.store offset=28 - local.get 0 - local.get 3 - i32.const 2 - i32.shl - i32.add - local.set 4 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.load offset=412 - local.tee 5 - i32.const 1 - local.get 3 - i32.shl - local.tee 6 - i32.and - br_if 0 (;@2;) - local.get 0 - local.get 5 - local.get 6 - i32.or - i32.store offset=412 - local.get 1 - local.get 4 - i32.store offset=24 - local.get 4 - local.get 1 - i32.store - br 1 (;@1;) - end - local.get 2 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - i32.const 31 - i32.and - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 4 - i32.load - local.set 4 - loop ;; label = @2 - block ;; label = @3 - local.get 4 - local.tee 0 - i32.load offset=4 - i32.const -8 - i32.and - local.get 2 - i32.ne - br_if 0 (;@3;) - local.get 0 - i32.load offset=8 - local.tee 3 - local.get 1 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - local.get 1 - i32.const 0 - i32.store offset=24 - local.get 1 - local.get 0 - i32.store offset=12 - local.get 1 - local.get 3 - i32.store offset=8 - return - end - local.get 3 - i32.const 29 - i32.shr_u - local.set 4 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 0 - local.get 4 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@2;) - end - local.get 5 - local.get 1 - i32.store - local.get 1 - local.get 0 - i32.store offset=24 - end - local.get 1 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 1 - i32.store offset=8 - ) - (func $dlmalloc::dlmalloc::Dlmalloc::free (;3;) (type 1) (param i32 i32) - (local i32 i32 i32 i32 i32) - local.get 1 - i32.const -8 - i32.add - local.tee 2 - local.get 1 - i32.const -4 - i32.add - i32.load - local.tee 3 - i32.const -8 - i32.and - local.tee 1 - i32.add - local.set 4 - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.const 1 - i32.and - br_if 0 (;@6;) - local.get 3 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@5;) - local.get 2 - i32.load - local.tee 3 - local.get 1 - i32.add - local.set 1 - block ;; label = @7 - local.get 2 - local.get 3 - i32.sub - local.tee 2 - local.get 0 - i32.load offset=424 - i32.ne - br_if 0 (;@7;) - local.get 4 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 1 (;@6;) - local.get 4 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 2 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.store offset=416 - local.get 4 - local.get 1 - i32.store - return - end - block ;; label = @7 - local.get 3 - i32.const 256 - i32.lt_u - br_if 0 (;@7;) - local.get 0 - local.get 2 - call $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk - br 1 (;@6;) - end - block ;; label = @7 - local.get 2 - i32.load offset=12 - local.tee 5 - local.get 2 - i32.load offset=8 - local.tee 6 - i32.eq - br_if 0 (;@7;) - local.get 6 - local.get 5 - i32.store offset=12 - local.get 5 - local.get 6 - i32.store offset=8 - br 1 (;@6;) - end - local.get 0 - local.get 0 - i32.load offset=408 - i32.const -2 - local.get 3 - i32.const 3 - i32.shr_u - i32.rotl - i32.and - i32.store offset=408 - end - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - local.get 4 - i32.load offset=4 - local.tee 3 - i32.const 2 - i32.and - br_if 0 (;@8;) - local.get 4 - local.get 0 - i32.load offset=428 - i32.eq - br_if 2 (;@6;) - local.get 4 - local.get 0 - i32.load offset=424 - i32.eq - br_if 7 (;@1;) - local.get 3 - i32.const -8 - i32.and - local.tee 5 - local.get 1 - i32.add - local.set 1 - block ;; label = @9 - block ;; label = @10 - local.get 5 - i32.const 256 - i32.lt_u - br_if 0 (;@10;) - local.get 0 - local.get 4 - call $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk - br 1 (;@9;) - end - block ;; label = @10 - local.get 4 - i32.load offset=12 - local.tee 5 - local.get 4 - i32.load offset=8 - local.tee 4 - i32.eq - br_if 0 (;@10;) - local.get 4 - local.get 5 - i32.store offset=12 - local.get 5 - local.get 4 - i32.store offset=8 - br 1 (;@9;) - end - local.get 0 - local.get 0 - i32.load offset=408 - i32.const -2 - local.get 3 - i32.const 3 - i32.shr_u - i32.rotl - i32.and - i32.store offset=408 - end - local.get 2 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 2 - local.get 0 - i32.load offset=424 - i32.ne - br_if 1 (;@7;) - local.get 0 - local.get 1 - i32.store offset=416 - return - end - local.get 4 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 2 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 1 - i32.add - local.get 1 - i32.store - end - local.get 1 - i32.const 256 - i32.lt_u - br_if 2 (;@4;) - local.get 0 - local.get 2 - local.get 1 - call $dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk - local.get 0 - local.get 0 - i32.load offset=448 - i32.const -1 - i32.add - local.tee 2 - i32.store offset=448 - local.get 2 - br_if 1 (;@5;) - local.get 0 - i32.const 136 - i32.add - i32.load - local.tee 1 - br_if 3 (;@3;) - i32.const 0 - local.set 2 - br 4 (;@2;) - end - local.get 0 - local.get 2 - i32.store offset=428 - local.get 0 - local.get 0 - i32.load offset=420 - local.get 1 - i32.add - local.tee 1 - i32.store offset=420 - local.get 2 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @6 - local.get 2 - local.get 0 - i32.load offset=424 - i32.ne - br_if 0 (;@6;) - local.get 0 - i32.const 0 - i32.store offset=416 - local.get 0 - i32.const 0 - i32.store offset=424 - end - local.get 1 - local.get 0 - i32.load offset=440 - i32.le_u - br_if 0 (;@5;) - block ;; label = @6 - local.get 1 - i32.const 41 - i32.lt_u - br_if 0 (;@6;) - local.get 0 - i32.const 128 - i32.add - local.set 1 - loop ;; label = @7 - block ;; label = @8 - local.get 1 - i32.load - local.tee 4 - local.get 2 - i32.gt_u - br_if 0 (;@8;) - local.get 4 - local.get 1 - i32.load offset=4 - i32.add - local.get 2 - i32.gt_u - br_if 2 (;@6;) - end - local.get 1 - i32.load offset=8 - local.tee 1 - br_if 0 (;@7;) - end - end - block ;; label = @6 - block ;; label = @7 - local.get 0 - i32.const 136 - i32.add - i32.load - local.tee 1 - br_if 0 (;@7;) - i32.const 0 - local.set 2 - br 1 (;@6;) - end - i32.const 0 - local.set 2 - loop ;; label = @7 - local.get 2 - i32.const 1 - i32.add - local.set 2 - local.get 1 - i32.load offset=8 - local.tee 1 - br_if 0 (;@7;) - end - end - local.get 0 - i32.const -1 - i32.store offset=440 - local.get 0 - local.get 2 - i32.const 4095 - local.get 2 - i32.const 4095 - i32.gt_u - select - i32.store offset=448 - end - return - end - local.get 0 - local.get 1 - i32.const -8 - i32.and - i32.add - i32.const 144 - i32.add - local.set 4 - block ;; label = @4 - block ;; label = @5 - local.get 0 - i32.load offset=408 - local.tee 3 - i32.const 1 - local.get 1 - i32.const 3 - i32.shr_u - i32.shl - local.tee 1 - i32.and - br_if 0 (;@5;) - local.get 0 - local.get 3 - local.get 1 - i32.or - i32.store offset=408 - local.get 4 - local.set 0 - br 1 (;@4;) - end - local.get 4 - i32.load offset=8 - local.set 0 - end - local.get 4 - local.get 2 - i32.store offset=8 - local.get 0 - local.get 2 - i32.store offset=12 - local.get 2 - local.get 4 - i32.store offset=12 - local.get 2 - local.get 0 - i32.store offset=8 - return - end - i32.const 0 - local.set 2 - loop ;; label = @3 - local.get 2 - i32.const 1 - i32.add - local.set 2 - local.get 1 - i32.load offset=8 - local.tee 1 - br_if 0 (;@3;) - end - end - local.get 0 - local.get 2 - i32.const 4095 - local.get 2 - i32.const 4095 - i32.gt_u - select - i32.store offset=448 - return - end - local.get 2 - local.get 0 - i32.load offset=416 - local.get 1 - i32.add - local.tee 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 2 - i32.store offset=424 - local.get 0 - local.get 1 - i32.store offset=416 - local.get 2 - local.get 1 - i32.add - local.get 1 - i32.store - ) - (func $dlmalloc::dlmalloc::Dlmalloc::malloc (;4;) (type 2) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i64) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - local.get 1 - i32.const 245 - i32.lt_u - br_if 0 (;@6;) - i32.const 0 - local.set 2 - local.get 1 - i32.const -65587 - i32.ge_u - br_if 5 (;@1;) - local.get 1 - i32.const 11 - i32.add - local.tee 3 - i32.const -8 - i32.and - local.set 4 - local.get 0 - i32.load offset=412 - local.tee 5 - i32.eqz - br_if 4 (;@2;) - i32.const 0 - local.set 1 - i32.const 0 - local.set 6 - block ;; label = @7 - local.get 4 - i32.const 256 - i32.lt_u - br_if 0 (;@7;) - i32.const 31 - local.set 6 - local.get 4 - i32.const 16777215 - i32.gt_u - br_if 0 (;@7;) - local.get 4 - i32.const 6 - local.get 3 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 6 - end - i32.const 0 - local.get 4 - i32.sub - local.set 3 - block ;; label = @7 - local.get 0 - local.get 6 - i32.const 2 - i32.shl - i32.add - i32.load - local.tee 7 - br_if 0 (;@7;) - i32.const 0 - local.set 8 - br 2 (;@5;) - end - i32.const 0 - local.set 1 - local.get 4 - i32.const 0 - i32.const 25 - local.get 6 - i32.const 1 - i32.shr_u - i32.sub - local.get 6 - i32.const 31 - i32.eq - select - i32.shl - local.set 2 - i32.const 0 - local.set 8 - loop ;; label = @7 - block ;; label = @8 - local.get 7 - i32.load offset=4 - i32.const -8 - i32.and - local.tee 9 - local.get 4 - i32.lt_u - br_if 0 (;@8;) - local.get 9 - local.get 4 - i32.sub - local.tee 9 - local.get 3 - i32.ge_u - br_if 0 (;@8;) - local.get 9 - local.set 3 - local.get 7 - local.set 8 - local.get 9 - br_if 0 (;@8;) - i32.const 0 - local.set 3 - local.get 7 - local.set 8 - local.get 7 - local.set 1 - br 4 (;@4;) - end - local.get 7 - i32.const 20 - i32.add - i32.load - local.tee 9 - local.get 1 - local.get 9 - local.get 7 - local.get 2 - i32.const 29 - i32.shr_u - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - i32.load - local.tee 7 - i32.ne - select - local.get 1 - local.get 9 - select - local.set 1 - local.get 2 - i32.const 1 - i32.shl - local.set 2 - local.get 7 - i32.eqz - br_if 2 (;@5;) - br 0 (;@7;) - end - end - block ;; label = @6 - local.get 0 - i32.load offset=408 - local.tee 8 - i32.const 16 - local.get 1 - i32.const 11 - i32.add - i32.const -8 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.tee 4 - i32.const 3 - i32.shr_u - local.tee 3 - i32.shr_u - local.tee 1 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@6;) - block ;; label = @7 - block ;; label = @8 - local.get 0 - local.get 1 - i32.const -1 - i32.xor - i32.const 1 - i32.and - local.get 3 - i32.add - local.tee 4 - i32.const 3 - i32.shl - i32.add - local.tee 7 - i32.const 152 - i32.add - i32.load - local.tee 1 - i32.load offset=8 - local.tee 3 - local.get 7 - i32.const 144 - i32.add - local.tee 7 - i32.eq - br_if 0 (;@8;) - local.get 3 - local.get 7 - i32.store offset=12 - local.get 7 - local.get 3 - i32.store offset=8 - br 1 (;@7;) - end - local.get 0 - local.get 8 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=408 - end - local.get 1 - local.get 4 - i32.const 3 - i32.shl - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 1 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 8 - i32.add - return - end - local.get 4 - local.get 0 - i32.load offset=416 - i32.le_u - br_if 3 (;@2;) - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - local.get 1 - br_if 0 (;@8;) - local.get 0 - i32.load offset=412 - local.tee 1 - i32.eqz - br_if 6 (;@2;) - local.get 0 - local.get 1 - i32.ctz - i32.const 2 - i32.shl - i32.add - i32.load - local.tee 7 - i32.load offset=4 - i32.const -8 - i32.and - local.get 4 - i32.sub - local.set 3 - local.get 7 - local.set 8 - block ;; label = @9 - block ;; label = @10 - loop ;; label = @11 - block ;; label = @12 - local.get 7 - i32.load offset=16 - local.tee 1 - br_if 0 (;@12;) - local.get 7 - i32.const 20 - i32.add - i32.load - local.tee 1 - br_if 0 (;@12;) - local.get 0 - local.get 8 - call $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk - local.get 3 - i32.const 16 - i32.lt_u - br_if 3 (;@9;) - local.get 8 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 8 - local.get 4 - i32.add - local.tee 4 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - i32.add - local.get 3 - i32.store - local.get 0 - i32.load offset=416 - local.tee 2 - br_if 2 (;@10;) - br 5 (;@7;) - end - local.get 1 - i32.load offset=4 - i32.const -8 - i32.and - local.get 4 - i32.sub - local.tee 7 - local.get 3 - local.get 7 - local.get 3 - i32.lt_u - local.tee 7 - select - local.set 3 - local.get 1 - local.get 8 - local.get 7 - select - local.set 8 - local.get 1 - local.set 7 - br 0 (;@11;) - end - end - local.get 0 - local.get 2 - i32.const -8 - i32.and - i32.add - i32.const 144 - i32.add - local.set 7 - local.get 0 - i32.load offset=424 - local.set 1 - block ;; label = @10 - block ;; label = @11 - local.get 0 - i32.load offset=408 - local.tee 9 - i32.const 1 - local.get 2 - i32.const 3 - i32.shr_u - i32.shl - local.tee 2 - i32.and - br_if 0 (;@11;) - local.get 0 - local.get 9 - local.get 2 - i32.or - i32.store offset=408 - local.get 7 - local.set 2 - br 1 (;@10;) - end - local.get 7 - i32.load offset=8 - local.set 2 - end - local.get 7 - local.get 1 - i32.store offset=8 - local.get 2 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 7 - i32.store offset=12 - local.get 1 - local.get 2 - i32.store offset=8 - br 2 (;@7;) - end - local.get 8 - local.get 3 - local.get 4 - i32.add - local.tee 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 8 - local.get 1 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 2 (;@6;) - end - block ;; label = @8 - block ;; label = @9 - local.get 0 - i32.const 144 - i32.add - local.tee 9 - local.get 1 - local.get 3 - i32.shl - i32.const 2 - local.get 3 - i32.shl - local.tee 1 - i32.const 0 - local.get 1 - i32.sub - i32.or - i32.and - i32.ctz - local.tee 7 - i32.const 3 - i32.shl - i32.add - local.tee 3 - i32.load offset=8 - local.tee 1 - i32.load offset=8 - local.tee 2 - local.get 3 - i32.eq - br_if 0 (;@9;) - local.get 2 - local.get 3 - i32.store offset=12 - local.get 3 - local.get 2 - i32.store offset=8 - br 1 (;@8;) - end - local.get 0 - local.get 8 - i32.const -2 - local.get 7 - i32.rotl - i32.and - i32.store offset=408 - end - local.get 1 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 1 - local.get 4 - i32.add - local.tee 2 - local.get 7 - i32.const 3 - i32.shl - local.tee 7 - local.get 4 - i32.sub - local.tee 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 7 - i32.add - local.get 3 - i32.store - block ;; label = @8 - local.get 0 - i32.load offset=416 - local.tee 8 - i32.eqz - br_if 0 (;@8;) - local.get 9 - local.get 8 - i32.const -8 - i32.and - i32.add - local.set 7 - local.get 0 - i32.load offset=424 - local.set 4 - block ;; label = @9 - block ;; label = @10 - local.get 0 - i32.load offset=408 - local.tee 9 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 8 - i32.and - br_if 0 (;@10;) - local.get 0 - local.get 9 - local.get 8 - i32.or - i32.store offset=408 - local.get 7 - local.set 8 - br 1 (;@9;) - end - local.get 7 - i32.load offset=8 - local.set 8 - end - local.get 7 - local.get 4 - i32.store offset=8 - local.get 8 - local.get 4 - i32.store offset=12 - local.get 4 - local.get 7 - i32.store offset=12 - local.get 4 - local.get 8 - i32.store offset=8 - end - local.get 0 - local.get 2 - i32.store offset=424 - local.get 0 - local.get 3 - i32.store offset=416 - local.get 1 - i32.const 8 - i32.add - return - end - local.get 0 - local.get 4 - i32.store offset=424 - local.get 0 - local.get 3 - i32.store offset=416 - end - local.get 8 - i32.const 8 - i32.add - return - end - block ;; label = @5 - local.get 1 - local.get 8 - i32.or - br_if 0 (;@5;) - i32.const 0 - local.set 8 - i32.const 2 - local.get 6 - i32.shl - local.tee 1 - i32.const 0 - local.get 1 - i32.sub - i32.or - local.get 5 - i32.and - local.tee 1 - i32.eqz - br_if 3 (;@2;) - local.get 0 - local.get 1 - i32.ctz - i32.const 2 - i32.shl - i32.add - i32.load - local.set 1 - end - local.get 1 - i32.eqz - br_if 1 (;@3;) - end - loop ;; label = @4 - local.get 1 - local.get 8 - local.get 1 - i32.load offset=4 - i32.const -8 - i32.and - local.tee 7 - local.get 4 - i32.sub - local.tee 9 - local.get 3 - i32.lt_u - local.tee 6 - select - local.set 5 - local.get 7 - local.get 4 - i32.lt_u - local.set 2 - local.get 9 - local.get 3 - local.get 6 - select - local.set 9 - block ;; label = @5 - local.get 1 - i32.load offset=16 - local.tee 7 - br_if 0 (;@5;) - local.get 1 - i32.const 20 - i32.add - i32.load - local.set 7 - end - local.get 8 - local.get 5 - local.get 2 - select - local.set 8 - local.get 3 - local.get 9 - local.get 2 - select - local.set 3 - local.get 7 - local.set 1 - local.get 7 - br_if 0 (;@4;) - end - end - local.get 8 - i32.eqz - br_if 0 (;@2;) - block ;; label = @3 - local.get 0 - i32.load offset=416 - local.tee 1 - local.get 4 - i32.lt_u - br_if 0 (;@3;) - local.get 3 - local.get 1 - local.get 4 - i32.sub - i32.ge_u - br_if 1 (;@2;) - end - local.get 0 - local.get 8 - call $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 16 - i32.lt_u - br_if 0 (;@4;) - local.get 8 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 8 - local.get 4 - i32.add - local.tee 1 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @5 - local.get 3 - i32.const 256 - i32.lt_u - br_if 0 (;@5;) - local.get 0 - local.get 1 - local.get 3 - call $dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk - br 2 (;@3;) - end - local.get 0 - local.get 3 - i32.const -8 - i32.and - i32.add - i32.const 144 - i32.add - local.set 4 - block ;; label = @5 - block ;; label = @6 - local.get 0 - i32.load offset=408 - local.tee 7 - i32.const 1 - local.get 3 - i32.const 3 - i32.shr_u - i32.shl - local.tee 3 - i32.and - br_if 0 (;@6;) - local.get 0 - local.get 7 - local.get 3 - i32.or - i32.store offset=408 - local.get 4 - local.set 3 - br 1 (;@5;) - end - local.get 4 - i32.load offset=8 - local.set 3 - end - local.get 4 - local.get 1 - i32.store offset=8 - local.get 3 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 4 - i32.store offset=12 - local.get 1 - local.get 3 - i32.store offset=8 - br 1 (;@3;) - end - local.get 8 - local.get 3 - local.get 4 - i32.add - local.tee 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 8 - local.get 1 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - end - local.get 8 - i32.const 8 - i32.add - return - end - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - local.get 0 - i32.load offset=416 - local.tee 1 - local.get 4 - i32.ge_u - br_if 0 (;@9;) - block ;; label = @10 - local.get 0 - i32.load offset=420 - local.tee 1 - local.get 4 - i32.gt_u - br_if 0 (;@10;) - i32.const 0 - local.set 2 - local.get 4 - i32.const 65583 - i32.add - local.tee 3 - i32.const 16 - i32.shr_u - memory.grow - local.tee 1 - i32.const -1 - i32.eq - local.tee 7 - br_if 9 (;@1;) - local.get 1 - i32.const 16 - i32.shl - local.tee 9 - i32.eqz - br_if 9 (;@1;) - local.get 0 - local.get 0 - i32.load offset=432 - i32.const 0 - local.get 3 - i32.const -65536 - i32.and - local.get 7 - select - local.tee 6 - i32.add - local.tee 1 - i32.store offset=432 - local.get 0 - local.get 0 - i32.load offset=436 - local.tee 3 - local.get 1 - local.get 3 - local.get 1 - i32.gt_u - select - i32.store offset=436 - block ;; label = @11 - local.get 0 - i32.load offset=428 - local.tee 3 - br_if 0 (;@11;) - block ;; label = @12 - block ;; label = @13 - local.get 0 - i32.load offset=444 - local.tee 1 - i32.eqz - br_if 0 (;@13;) - local.get 1 - local.get 9 - i32.le_u - br_if 1 (;@12;) - end - local.get 0 - local.get 9 - i32.store offset=444 - end - local.get 0 - i32.const 4095 - i32.store offset=448 - local.get 0 - local.get 9 - i32.store offset=128 - i32.const 0 - local.set 3 - local.get 0 - i32.const 140 - i32.add - i32.const 0 - i32.store - local.get 0 - i32.const 132 - i32.add - local.get 6 - i32.store - loop ;; label = @12 - local.get 0 - local.get 3 - i32.add - local.tee 1 - i32.const 164 - i32.add - local.get 1 - i32.const 152 - i32.add - local.tee 7 - i32.store - local.get 7 - local.get 1 - i32.const 144 - i32.add - local.tee 8 - i32.store - local.get 1 - i32.const 156 - i32.add - local.get 8 - i32.store - local.get 1 - i32.const 172 - i32.add - local.get 1 - i32.const 160 - i32.add - local.tee 8 - i32.store - local.get 8 - local.get 7 - i32.store - local.get 1 - i32.const 180 - i32.add - local.get 1 - i32.const 168 - i32.add - local.tee 7 - i32.store - local.get 7 - local.get 8 - i32.store - local.get 1 - i32.const 176 - i32.add - local.get 7 - i32.store - local.get 3 - i32.const 32 - i32.add - local.tee 3 - i32.const 256 - i32.ne - br_if 0 (;@12;) - end - local.get 9 - local.get 6 - i32.const -40 - i32.add - local.tee 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 9 - i32.store offset=428 - local.get 0 - i32.const 2097152 - i32.store offset=440 - local.get 0 - local.get 1 - i32.store offset=420 - local.get 9 - local.get 1 - i32.add - i32.const 40 - i32.store offset=4 - br 9 (;@2;) - end - local.get 0 - i32.const 128 - i32.add - local.tee 5 - local.set 1 - block ;; label = @11 - block ;; label = @12 - loop ;; label = @13 - local.get 1 - i32.load - local.tee 7 - local.get 1 - i32.load offset=4 - local.tee 8 - i32.add - local.get 9 - i32.eq - br_if 1 (;@12;) - local.get 1 - i32.load offset=8 - local.tee 1 - br_if 0 (;@13;) - br 2 (;@11;) - end - end - local.get 3 - local.get 9 - i32.ge_u - br_if 0 (;@11;) - local.get 7 - local.get 3 - i32.gt_u - br_if 0 (;@11;) - local.get 1 - i32.load offset=12 - i32.eqz - br_if 3 (;@8;) - end - local.get 0 - local.get 0 - i32.load offset=444 - local.tee 1 - local.get 9 - local.get 1 - local.get 9 - i32.lt_u - select - i32.store offset=444 - local.get 9 - local.get 6 - i32.add - local.set 7 - local.get 5 - local.set 1 - block ;; label = @11 - block ;; label = @12 - block ;; label = @13 - loop ;; label = @14 - local.get 1 - i32.load - local.get 7 - i32.eq - br_if 1 (;@13;) - local.get 1 - i32.load offset=8 - local.tee 1 - br_if 0 (;@14;) - br 2 (;@12;) - end - end - local.get 1 - i32.load offset=12 - i32.eqz - br_if 1 (;@11;) - end - local.get 5 - local.set 1 - block ;; label = @12 - loop ;; label = @13 - block ;; label = @14 - local.get 1 - i32.load - local.tee 7 - local.get 3 - i32.gt_u - br_if 0 (;@14;) - local.get 7 - local.get 1 - i32.load offset=4 - i32.add - local.tee 7 - local.get 3 - i32.gt_u - br_if 2 (;@12;) - end - local.get 1 - i32.load offset=8 - local.set 1 - br 0 (;@13;) - end - end - local.get 9 - local.get 6 - i32.const -40 - i32.add - local.tee 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 9 - i32.store offset=428 - local.get 0 - i32.const 2097152 - i32.store offset=440 - local.get 0 - local.get 1 - i32.store offset=420 - local.get 9 - local.get 1 - i32.add - i32.const 40 - i32.store offset=4 - local.get 3 - local.get 7 - i32.const -32 - i32.add - i32.const -8 - i32.and - i32.const -8 - i32.add - local.tee 1 - local.get 1 - local.get 3 - i32.const 16 - i32.add - i32.lt_u - select - local.tee 8 - i32.const 27 - i32.store offset=4 - local.get 5 - i64.load align=4 - local.set 10 - local.get 8 - i32.const 16 - i32.add - local.get 5 - i32.const 8 - i32.add - i64.load align=4 - i64.store align=4 - local.get 8 - local.get 10 - i64.store offset=8 align=4 - local.get 0 - i32.const 140 - i32.add - i32.const 0 - i32.store - local.get 0 - i32.const 132 - i32.add - local.get 6 - i32.store - local.get 0 - local.get 9 - i32.store offset=128 - local.get 0 - i32.const 136 - i32.add - local.get 8 - i32.const 8 - i32.add - i32.store - local.get 8 - i32.const 28 - i32.add - local.set 1 - loop ;; label = @12 - local.get 1 - i32.const 7 - i32.store - local.get 1 - i32.const 4 - i32.add - local.tee 1 - local.get 7 - i32.lt_u - br_if 0 (;@12;) - end - local.get 8 - local.get 3 - i32.eq - br_if 9 (;@2;) - local.get 8 - local.get 8 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - local.get 3 - local.get 8 - local.get 3 - i32.sub - local.tee 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 8 - local.get 1 - i32.store - block ;; label = @12 - local.get 1 - i32.const 256 - i32.lt_u - br_if 0 (;@12;) - local.get 0 - local.get 3 - local.get 1 - call $dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk - br 10 (;@2;) - end - local.get 0 - local.get 1 - i32.const -8 - i32.and - i32.add - i32.const 144 - i32.add - local.set 7 - block ;; label = @12 - block ;; label = @13 - local.get 0 - i32.load offset=408 - local.tee 8 - i32.const 1 - local.get 1 - i32.const 3 - i32.shr_u - i32.shl - local.tee 1 - i32.and - br_if 0 (;@13;) - local.get 0 - local.get 8 - local.get 1 - i32.or - i32.store offset=408 - local.get 7 - local.set 1 - br 1 (;@12;) - end - local.get 7 - i32.load offset=8 - local.set 1 - end - local.get 7 - local.get 3 - i32.store offset=8 - local.get 1 - local.get 3 - i32.store offset=12 - local.get 3 - local.get 7 - i32.store offset=12 - local.get 3 - local.get 1 - i32.store offset=8 - br 9 (;@2;) - end - local.get 1 - local.get 9 - i32.store - local.get 1 - local.get 1 - i32.load offset=4 - local.get 6 - i32.add - i32.store offset=4 - local.get 9 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 7 - local.get 9 - local.get 4 - i32.add - local.tee 1 - i32.sub - local.set 4 - local.get 7 - local.get 0 - i32.load offset=428 - i32.eq - br_if 3 (;@7;) - local.get 7 - local.get 0 - i32.load offset=424 - i32.eq - br_if 4 (;@6;) - block ;; label = @11 - local.get 7 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 1 - i32.ne - br_if 0 (;@11;) - block ;; label = @12 - block ;; label = @13 - local.get 3 - i32.const -8 - i32.and - local.tee 8 - i32.const 256 - i32.lt_u - br_if 0 (;@13;) - local.get 0 - local.get 7 - call $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk - br 1 (;@12;) - end - block ;; label = @13 - local.get 7 - i32.load offset=12 - local.tee 2 - local.get 7 - i32.load offset=8 - local.tee 6 - i32.eq - br_if 0 (;@13;) - local.get 6 - local.get 2 - i32.store offset=12 - local.get 2 - local.get 6 - i32.store offset=8 - br 1 (;@12;) - end - local.get 0 - local.get 0 - i32.load offset=408 - i32.const -2 - local.get 3 - i32.const 3 - i32.shr_u - i32.rotl - i32.and - i32.store offset=408 - end - local.get 8 - local.get 4 - i32.add - local.set 4 - local.get 7 - local.get 8 - i32.add - local.tee 7 - i32.load offset=4 - local.set 3 - end - local.get 7 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 1 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 4 - i32.add - local.get 4 - i32.store - block ;; label = @11 - local.get 4 - i32.const 256 - i32.lt_u - br_if 0 (;@11;) - local.get 0 - local.get 1 - local.get 4 - call $dlmalloc::dlmalloc::Dlmalloc::insert_large_chunk - br 8 (;@3;) - end - local.get 0 - local.get 4 - i32.const -8 - i32.and - i32.add - i32.const 144 - i32.add - local.set 3 - block ;; label = @11 - block ;; label = @12 - local.get 0 - i32.load offset=408 - local.tee 7 - i32.const 1 - local.get 4 - i32.const 3 - i32.shr_u - i32.shl - local.tee 4 - i32.and - br_if 0 (;@12;) - local.get 0 - local.get 7 - local.get 4 - i32.or - i32.store offset=408 - local.get 3 - local.set 4 - br 1 (;@11;) - end - local.get 3 - i32.load offset=8 - local.set 4 - end - local.get 3 - local.get 1 - i32.store offset=8 - local.get 4 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 3 - i32.store offset=12 - local.get 1 - local.get 4 - i32.store offset=8 - br 7 (;@3;) - end - local.get 0 - local.get 1 - local.get 4 - i32.sub - local.tee 3 - i32.store offset=420 - local.get 0 - local.get 0 - i32.load offset=428 - local.tee 1 - local.get 4 - i32.add - local.tee 7 - i32.store offset=428 - local.get 7 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 1 - i32.const 8 - i32.add - local.set 2 - br 8 (;@1;) - end - local.get 0 - i32.load offset=424 - local.set 3 - local.get 1 - local.get 4 - i32.sub - local.tee 7 - i32.const 16 - i32.lt_u - br_if 3 (;@5;) - local.get 0 - local.get 7 - i32.store offset=416 - local.get 0 - local.get 3 - local.get 4 - i32.add - local.tee 8 - i32.store offset=424 - local.get 8 - local.get 7 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 1 - i32.add - local.get 7 - i32.store - local.get 3 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - br 4 (;@4;) - end - local.get 1 - local.get 8 - local.get 6 - i32.add - i32.store offset=4 - local.get 0 - i32.load offset=428 - local.tee 1 - i32.const 15 - i32.add - i32.const -8 - i32.and - local.tee 3 - i32.const -8 - i32.add - local.tee 7 - local.get 1 - local.get 3 - i32.sub - local.get 0 - i32.load offset=420 - local.get 6 - i32.add - local.tee 3 - i32.add - i32.const 8 - i32.add - local.tee 8 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 2097152 - i32.store offset=440 - local.get 0 - local.get 7 - i32.store offset=428 - local.get 0 - local.get 8 - i32.store offset=420 - local.get 1 - local.get 3 - i32.add - i32.const 40 - i32.store offset=4 - br 5 (;@2;) - end - local.get 0 - local.get 1 - i32.store offset=428 - local.get 0 - local.get 0 - i32.load offset=420 - local.get 4 - i32.add - local.tee 4 - i32.store offset=420 - local.get 1 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - br 3 (;@3;) - end - local.get 1 - local.get 0 - i32.load offset=416 - local.get 4 - i32.add - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.store offset=424 - local.get 0 - local.get 4 - i32.store offset=416 - local.get 1 - local.get 4 - i32.add - local.get 4 - i32.store - br 2 (;@3;) - end - local.get 0 - i32.const 0 - i32.store offset=424 - local.get 0 - i32.const 0 - i32.store offset=416 - local.get 3 - local.get 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 1 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - end - local.get 3 - i32.const 8 - i32.add - return - end - local.get 9 - i32.const 8 - i32.add - return - end - local.get 0 - i32.load offset=420 - local.tee 1 - local.get 4 - i32.le_u - br_if 0 (;@1;) - local.get 0 - local.get 1 - local.get 4 - i32.sub - local.tee 3 - i32.store offset=420 - local.get 0 - local.get 0 - i32.load offset=428 - local.tee 1 - local.get 4 - i32.add - local.tee 7 - i32.store offset=428 - local.get 7 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 1 - i32.const 8 - i32.add - return - end - local.get 2 - ) - (func $dlmalloc::dlmalloc::Dlmalloc::memalign (;5;) (type 3) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32 i32) - i32.const 0 - local.set 3 - block ;; label = @1 - i32.const -65587 - local.get 1 - i32.const 16 - local.get 1 - i32.const 16 - i32.gt_u - select - local.tee 1 - i32.sub - local.get 2 - i32.le_u - br_if 0 (;@1;) - local.get 0 - local.get 1 - i32.const 16 - local.get 2 - i32.const 11 - i32.add - i32.const -8 - i32.and - local.get 2 - i32.const 11 - i32.lt_u - select - local.tee 4 - i32.add - i32.const 12 - i32.add - call $dlmalloc::dlmalloc::Dlmalloc::malloc - local.tee 2 - i32.eqz - br_if 0 (;@1;) - local.get 2 - i32.const -8 - i32.add - local.set 3 - block ;; label = @2 - block ;; label = @3 - local.get 1 - i32.const -1 - i32.add - local.tee 5 - local.get 2 - i32.and - br_if 0 (;@3;) - local.get 3 - local.set 1 - br 1 (;@2;) - end - local.get 2 - i32.const -4 - i32.add - local.tee 6 - i32.load - local.tee 7 - i32.const -8 - i32.and - local.get 5 - local.get 2 - i32.add - i32.const 0 - local.get 1 - i32.sub - i32.and - i32.const -8 - i32.add - local.tee 2 - i32.const 0 - local.get 1 - local.get 2 - local.get 3 - i32.sub - i32.const 16 - i32.gt_u - select - i32.add - local.tee 1 - local.get 3 - i32.sub - local.tee 2 - i32.sub - local.set 5 - block ;; label = @3 - local.get 7 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 1 - local.get 5 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 1 - local.get 5 - i32.add - local.tee 5 - local.get 5 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 6 - local.get 2 - local.get 6 - i32.load - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 3 - local.get 2 - i32.add - local.tee 5 - local.get 5 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 3 - local.get 2 - call $dlmalloc::dlmalloc::Dlmalloc::dispose_chunk - br 1 (;@2;) - end - local.get 3 - i32.load - local.set 3 - local.get 1 - local.get 5 - i32.store offset=4 - local.get 1 - local.get 3 - local.get 2 - i32.add - i32.store - end - block ;; label = @2 - local.get 1 - i32.load offset=4 - local.tee 2 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - i32.const -8 - i32.and - local.tee 3 - local.get 4 - i32.const 16 - i32.add - i32.le_u - br_if 0 (;@2;) - local.get 1 - local.get 4 - local.get 2 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 1 - local.get 4 - i32.add - local.tee 2 - local.get 3 - local.get 4 - i32.sub - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 1 - local.get 3 - i32.add - local.tee 3 - local.get 3 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 2 - local.get 4 - call $dlmalloc::dlmalloc::Dlmalloc::dispose_chunk - end - local.get 1 - i32.const 8 - i32.add - local.set 3 - end - local.get 3 - ) - (func $__main (;6;) (type 4) (result i32) - (local i32 i32 i32) - global.get $__stack_pointer - i32.const 16 - i32.sub - local.tee 0 - global.set $__stack_pointer - local.get 0 - i32.const 0 - i32.store offset=12 - local.get 0 - i64.const 4 - i64.store offset=4 align=4 - local.get 0 - i32.const 4 - i32.add - i32.const 0 - call $alloc::raw_vec::RawVec::reserve_for_push - local.get 0 - i32.load offset=4 - local.tee 1 - local.get 0 - i32.load offset=12 - local.tee 2 - i32.const 2 - i32.shl - i32.add - i32.const 1 - i32.store - block ;; label = @1 - local.get 2 - i32.const -1 - i32.eq - br_if 0 (;@1;) - block ;; label = @2 - local.get 0 - i32.load offset=8 - i32.eqz - br_if 0 (;@2;) - i32.const 1048580 - local.get 1 - call $dlmalloc::dlmalloc::Dlmalloc::free - end - local.get 0 - i32.const 16 - i32.add - global.set $__stack_pointer - i32.const 1 - return - end - unreachable - unreachable - ) - (func $__rust_realloc (;7;) (type 5) (param i32 i32 i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - local.get 2 - i32.const 9 - i32.lt_u - br_if 0 (;@4;) - i32.const 1048580 - local.get 2 - local.get 3 - call $dlmalloc::dlmalloc::Dlmalloc::memalign - local.tee 2 - br_if 1 (;@3;) - i32.const 0 - return - end - i32.const 0 - local.set 2 - local.get 3 - i32.const -65588 - i32.gt_u - br_if 1 (;@2;) - i32.const 16 - local.get 3 - i32.const 11 - i32.add - i32.const -8 - i32.and - local.get 3 - i32.const 11 - i32.lt_u - select - local.set 1 - local.get 0 - i32.const -4 - i32.add - local.tee 4 - i32.load - local.tee 5 - i32.const -8 - i32.and - local.set 6 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - local.get 5 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@11;) - local.get 0 - i32.const -8 - i32.add - local.tee 7 - local.get 6 - i32.add - local.set 8 - local.get 6 - local.get 1 - i32.ge_u - br_if 1 (;@10;) - local.get 8 - i32.const 0 - i32.load offset=1049008 - i32.eq - br_if 6 (;@5;) - local.get 8 - i32.const 0 - i32.load offset=1049004 - i32.eq - br_if 4 (;@7;) - local.get 8 - i32.load offset=4 - local.tee 5 - i32.const 2 - i32.and - br_if 7 (;@4;) - local.get 5 - i32.const -8 - i32.and - local.tee 9 - local.get 6 - i32.add - local.tee 6 - local.get 1 - i32.lt_u - br_if 7 (;@4;) - local.get 6 - local.get 1 - i32.sub - local.set 3 - local.get 9 - i32.const 256 - i32.lt_u - br_if 2 (;@9;) - i32.const 1048580 - local.get 8 - call $dlmalloc::dlmalloc::Dlmalloc::unlink_large_chunk - br 3 (;@8;) - end - local.get 1 - i32.const 256 - i32.lt_u - br_if 6 (;@4;) - local.get 6 - local.get 1 - i32.const 4 - i32.or - i32.lt_u - br_if 6 (;@4;) - local.get 6 - local.get 1 - i32.sub - i32.const 131073 - i32.ge_u - br_if 6 (;@4;) - local.get 0 - return - end - local.get 6 - local.get 1 - i32.sub - local.tee 3 - i32.const 16 - i32.ge_u - br_if 3 (;@6;) - local.get 0 - return - end - block ;; label = @9 - local.get 8 - i32.load offset=12 - local.tee 2 - local.get 8 - i32.load offset=8 - local.tee 8 - i32.eq - br_if 0 (;@9;) - local.get 8 - local.get 2 - i32.store offset=12 - local.get 2 - local.get 8 - i32.store offset=8 - br 1 (;@8;) - end - i32.const 0 - i32.const 0 - i32.load offset=1048988 - i32.const -2 - local.get 5 - i32.const 3 - i32.shr_u - i32.rotl - i32.and - i32.store offset=1048988 - end - block ;; label = @8 - local.get 3 - i32.const 16 - i32.lt_u - br_if 0 (;@8;) - local.get 4 - local.get 1 - local.get 4 - i32.load - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 7 - local.get 1 - i32.add - local.tee 2 - local.get 3 - i32.const 3 - i32.or - i32.store offset=4 - local.get 7 - local.get 6 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 1048580 - local.get 2 - local.get 3 - call $dlmalloc::dlmalloc::Dlmalloc::dispose_chunk - local.get 0 - return - end - local.get 4 - local.get 6 - local.get 4 - i32.load - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 7 - local.get 6 - i32.add - local.tee 3 - local.get 3 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - i32.const 0 - i32.load offset=1048996 - local.get 6 - i32.add - local.tee 6 - local.get 1 - i32.lt_u - br_if 2 (;@4;) - block ;; label = @7 - block ;; label = @8 - local.get 6 - local.get 1 - i32.sub - local.tee 3 - i32.const 15 - i32.gt_u - br_if 0 (;@8;) - local.get 4 - local.get 5 - i32.const 1 - i32.and - local.get 6 - i32.or - i32.const 2 - i32.or - i32.store - local.get 7 - local.get 6 - i32.add - local.tee 3 - local.get 3 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 3 - i32.const 0 - local.set 2 - br 1 (;@7;) - end - local.get 4 - local.get 1 - local.get 5 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 7 - local.get 1 - i32.add - local.tee 2 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 7 - local.get 6 - i32.add - local.tee 1 - local.get 3 - i32.store - local.get 1 - local.get 1 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - end - i32.const 0 - local.get 2 - i32.store offset=1049004 - i32.const 0 - local.get 3 - i32.store offset=1048996 - local.get 0 - return - end - local.get 4 - local.get 1 - local.get 5 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 7 - local.get 1 - i32.add - local.tee 2 - local.get 3 - i32.const 3 - i32.or - i32.store offset=4 - local.get 8 - local.get 8 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 1048580 - local.get 2 - local.get 3 - call $dlmalloc::dlmalloc::Dlmalloc::dispose_chunk - local.get 0 - return - end - i32.const 0 - i32.load offset=1049000 - local.get 6 - i32.add - local.tee 6 - local.get 1 - i32.gt_u - br_if 3 (;@1;) - end - i32.const 1048580 - local.get 3 - call $dlmalloc::dlmalloc::Dlmalloc::malloc - local.tee 1 - i32.eqz - br_if 1 (;@2;) - local.get 1 - local.get 0 - i32.const -4 - i32.const -8 - local.get 4 - i32.load - local.tee 2 - i32.const 3 - i32.and - select - local.get 2 - i32.const -8 - i32.and - i32.add - local.tee 2 - local.get 3 - local.get 2 - local.get 3 - i32.lt_u - select - call $memcpy - local.set 3 - i32.const 1048580 - local.get 0 - call $dlmalloc::dlmalloc::Dlmalloc::free - local.get 3 - return - end - local.get 2 - local.get 0 - local.get 1 - local.get 3 - local.get 1 - local.get 3 - i32.lt_u - select - call $memcpy - drop - i32.const 1048580 - local.get 0 - call $dlmalloc::dlmalloc::Dlmalloc::free - end - local.get 2 - return - end - local.get 4 - local.get 1 - local.get 5 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - i32.const 0 - local.get 7 - local.get 1 - i32.add - local.tee 3 - i32.store offset=1049008 - i32.const 0 - local.get 6 - local.get 1 - i32.sub - local.tee 2 - i32.store offset=1049000 - local.get 3 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - ) - (func $alloc::raw_vec::finish_grow (;8;) (type 6) (param i32 i32 i32 i32) - (local i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 1 - i32.eqz - br_if 0 (;@3;) - local.get 2 - i32.const -1 - i32.le_s - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 3 - i32.load offset=4 - i32.eqz - br_if 0 (;@5;) - block ;; label = @6 - local.get 3 - i32.const 8 - i32.add - i32.load - local.tee 4 - br_if 0 (;@6;) - block ;; label = @7 - local.get 2 - br_if 0 (;@7;) - local.get 1 - local.set 3 - br 3 (;@4;) - end - i32.const 0 - i32.load8_u offset=1048576 - drop - block ;; label = @7 - local.get 1 - i32.const 9 - i32.lt_u - br_if 0 (;@7;) - i32.const 1048580 - local.get 1 - local.get 2 - call $dlmalloc::dlmalloc::Dlmalloc::memalign - local.set 3 - br 3 (;@4;) - end - i32.const 1048580 - local.get 2 - call $dlmalloc::dlmalloc::Dlmalloc::malloc - local.set 3 - br 2 (;@4;) - end - local.get 3 - i32.load - local.get 4 - local.get 1 - local.get 2 - call $__rust_realloc - local.set 3 - br 1 (;@4;) - end - block ;; label = @5 - local.get 2 - br_if 0 (;@5;) - local.get 1 - local.set 3 - br 1 (;@4;) - end - i32.const 0 - i32.load8_u offset=1048576 - drop - block ;; label = @5 - local.get 1 - i32.const 9 - i32.lt_u - br_if 0 (;@5;) - i32.const 1048580 - local.get 1 - local.get 2 - call $dlmalloc::dlmalloc::Dlmalloc::memalign - local.set 3 - br 1 (;@4;) - end - i32.const 1048580 - local.get 2 - call $dlmalloc::dlmalloc::Dlmalloc::malloc - local.set 3 - end - block ;; label = @4 - local.get 3 - i32.eqz - br_if 0 (;@4;) - local.get 0 - local.get 3 - i32.store offset=4 - local.get 0 - i32.const 8 - i32.add - local.get 2 - i32.store - local.get 0 - i32.const 0 - i32.store - return - end - local.get 0 - local.get 1 - i32.store offset=4 - local.get 0 - i32.const 8 - i32.add - local.get 2 - i32.store - br 2 (;@1;) - end - local.get 0 - i32.const 0 - i32.store offset=4 - local.get 0 - i32.const 8 - i32.add - local.get 2 - i32.store - br 1 (;@1;) - end - local.get 0 - i32.const 0 - i32.store offset=4 - end - local.get 0 - i32.const 1 - i32.store - ) - (func $alloc::raw_vec::RawVec::reserve_for_push (;9;) (type 1) (param i32 i32) - (local i32 i32 i32 i32) - global.get $__stack_pointer - i32.const 32 - i32.sub - local.tee 2 - global.set $__stack_pointer - block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.const 1 - i32.add - local.tee 1 - i32.eqz - br_if 0 (;@2;) - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 1 - i32.shl - local.tee 4 - local.get 1 - local.get 4 - local.get 1 - i32.gt_u - select - local.tee 1 - i32.const 4 - local.get 1 - i32.const 4 - i32.gt_u - select - local.tee 1 - i32.const 2 - i32.shl - local.set 4 - local.get 1 - i32.const 536870912 - i32.lt_u - i32.const 2 - i32.shl - local.set 5 - block ;; label = @3 - block ;; label = @4 - local.get 3 - br_if 0 (;@4;) - local.get 2 - i32.const 0 - i32.store offset=24 - br 1 (;@3;) - end - local.get 2 - i32.const 4 - i32.store offset=24 - local.get 2 - local.get 3 - i32.const 2 - i32.shl - i32.store offset=28 - local.get 2 - local.get 0 - i32.load - i32.store offset=20 - end - local.get 2 - i32.const 8 - i32.add - local.get 5 - local.get 4 - local.get 2 - i32.const 20 - i32.add - call $alloc::raw_vec::finish_grow - local.get 2 - i32.load offset=12 - local.set 3 - block ;; label = @3 - local.get 2 - i32.load offset=8 - br_if 0 (;@3;) - local.get 0 - local.get 1 - i32.store offset=4 - local.get 0 - local.get 3 - i32.store - br 2 (;@1;) - end - local.get 3 - i32.const -2147483647 - i32.eq - br_if 1 (;@1;) - end - unreachable - unreachable - end - local.get 2 - i32.const 32 - i32.add - global.set $__stack_pointer - ) - (func $vec_alloc (;10;) (type 4) (result i32) - (local i32 i32 i32) - global.get $__stack_pointer - i32.const 16 - i32.sub - local.tee 0 - global.set $__stack_pointer - local.get 0 - i32.const 0 - i32.store offset=12 - local.get 0 - i64.const 4 - i64.store offset=4 align=4 - local.get 0 - i32.const 4 - i32.add - i32.const 0 - call $alloc::raw_vec::RawVec::reserve_for_push - local.get 0 - i32.load offset=4 - local.tee 1 - local.get 0 - i32.load offset=12 - local.tee 2 - i32.const 2 - i32.shl - i32.add - i32.const 1 - i32.store - block ;; label = @1 - local.get 2 - i32.const -1 - i32.eq - br_if 0 (;@1;) - block ;; label = @2 - local.get 0 - i32.load offset=8 - i32.eqz - br_if 0 (;@2;) - i32.const 1048580 - local.get 1 - call $dlmalloc::dlmalloc::Dlmalloc::free - end - local.get 0 - i32.const 16 - i32.add - global.set $__stack_pointer - i32.const 1 - return - end - unreachable - unreachable - ) - (func $compiler_builtins::mem::memcpy (;11;) (type 3) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - local.get 2 - i32.const 16 - i32.ge_u - br_if 0 (;@2;) - local.get 0 - local.set 3 - br 1 (;@1;) - end - local.get 0 - i32.const 0 - local.get 0 - i32.sub - i32.const 3 - i32.and - local.tee 4 - i32.add - local.set 5 - block ;; label = @2 - local.get 4 - i32.eqz - br_if 0 (;@2;) - local.get 0 - local.set 3 - local.get 1 - local.set 6 - loop ;; label = @3 - local.get 3 - local.get 6 - i32.load8_u - i32.store8 - local.get 6 - i32.const 1 - i32.add - local.set 6 - local.get 3 - i32.const 1 - i32.add - local.tee 3 - local.get 5 - i32.lt_u - br_if 0 (;@3;) - end - end - local.get 5 - local.get 2 - local.get 4 - i32.sub - local.tee 7 - i32.const -4 - i32.and - local.tee 8 - i32.add - local.set 3 - block ;; label = @2 - block ;; label = @3 - local.get 1 - local.get 4 - i32.add - local.tee 9 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const 1 - i32.lt_s - br_if 1 (;@2;) - local.get 9 - i32.const 3 - i32.shl - local.tee 6 - i32.const 24 - i32.and - local.set 2 - local.get 9 - i32.const -4 - i32.and - local.tee 10 - i32.const 4 - i32.add - local.set 1 - i32.const 0 - local.get 6 - i32.sub - i32.const 24 - i32.and - local.set 4 - local.get 10 - i32.load - local.set 6 - loop ;; label = @4 - local.get 5 - local.get 6 - local.get 2 - i32.shr_u - local.get 1 - i32.load - local.tee 6 - local.get 4 - i32.shl - i32.or - i32.store - local.get 1 - i32.const 4 - i32.add - local.set 1 - local.get 5 - i32.const 4 - i32.add - local.tee 5 - local.get 3 - i32.lt_u - br_if 0 (;@4;) - br 2 (;@2;) - end - end - local.get 8 - i32.const 1 - i32.lt_s - br_if 0 (;@2;) - local.get 9 - local.set 1 - loop ;; label = @3 - local.get 5 - local.get 1 - i32.load - i32.store - local.get 1 - i32.const 4 - i32.add - local.set 1 - local.get 5 - i32.const 4 - i32.add - local.tee 5 - local.get 3 - i32.lt_u - br_if 0 (;@3;) - end - end - local.get 7 - i32.const 3 - i32.and - local.set 2 - local.get 9 - local.get 8 - i32.add - local.set 1 - end - block ;; label = @1 - local.get 2 - i32.eqz - br_if 0 (;@1;) - local.get 3 - local.get 2 - i32.add - local.set 5 - loop ;; label = @2 - local.get 3 - local.get 1 - i32.load8_u - i32.store8 - local.get 1 - i32.const 1 - i32.add - local.set 1 - local.get 3 - i32.const 1 - i32.add - local.tee 3 - local.get 5 - i32.lt_u - br_if 0 (;@2;) - end - end - local.get 0 - ) - (func $memcpy (;12;) (type 3) (param i32 i32 i32) (result i32) - local.get 0 - local.get 1 - local.get 2 - call $compiler_builtins::mem::memcpy - ) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1049032) - (global (;2;) i32 i32.const 1049040) - (export "memory" (memory 0)) - (export "__main" (func $__main)) - (export "vec_alloc" (func $vec_alloc)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/frontend-wasm/tests/expected/enum.hir b/frontend-wasm/tests/expected/enum.hir deleted file mode 100644 index 7d2c901d9..000000000 --- a/frontend-wasm/tests/expected/enum.hir +++ /dev/null @@ -1,51 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn __main() -> i32 { -block0: - v1 = const.i32 3 : i32; - v2 = const.i32 5 : i32; - v3 = const.i32 0 : i32; - v4 = call noname::match_enum(v1, v2, v3) : i32; - v5 = const.i32 3 : i32; - v6 = const.i32 5 : i32; - v7 = const.i32 1 : i32; - v8 = call noname::match_enum(v5, v6, v7) : i32; - v9 = add.wrapping v4, v8 : i32; - v10 = const.i32 3 : i32; - v11 = const.i32 5 : i32; - v12 = const.i32 2 : i32; - v13 = call noname::match_enum(v10, v11, v12) : i32; - v14 = add.wrapping v9, v13 : i32; - ret v14; -} - -pub fn match_enum(i32, i32, i32) -> i32 { -block0(v0: i32, v1: i32, v2: i32): - v4 = const.i32 255 : i32; - v5 = band v2, v4 : i32; - v6 = cast v5 : u32; - switch v6 { - 0 => block4, - 1 => block3, - 2 => block2, - _ => block4 - }; - -block2: - v9 = mul.wrapping v1, v0 : i32; - ret v9; - -block3: - v8 = sub.wrapping v0, v1 : i32; - ret v8; - -block4: - v7 = add.wrapping v1, v0 : i32; - ret v7; -} diff --git a/frontend-wasm/tests/expected/enum.wat b/frontend-wasm/tests/expected/enum.wat deleted file mode 100644 index 175388cb8..000000000 --- a/frontend-wasm/tests/expected/enum.wat +++ /dev/null @@ -1,52 +0,0 @@ -(module - (type (;0;) (func (param i32 i32 i32) (result i32))) - (type (;1;) (func (result i32))) - (func $match_enum (;0;) (type 0) (param i32 i32 i32) (result i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 255 - i32.and - br_table 0 (;@3;) 1 (;@2;) 2 (;@1;) 0 (;@3;) - end - local.get 1 - local.get 0 - i32.add - return - end - local.get 0 - local.get 1 - i32.sub - return - end - local.get 1 - local.get 0 - i32.mul - ) - (func $__main (;1;) (type 1) (result i32) - i32.const 3 - i32.const 5 - i32.const 0 - call $match_enum - i32.const 3 - i32.const 5 - i32.const 1 - call $match_enum - i32.add - i32.const 3 - i32.const 5 - i32.const 2 - call $match_enum - i32.add - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "match_enum" (func $match_enum)) - (export "__main" (func $__main)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/frontend-wasm/tests/expected/fib.hir b/frontend-wasm/tests/expected/fib.hir deleted file mode 100644 index ac0404dd6..000000000 --- a/frontend-wasm/tests/expected/fib.hir +++ /dev/null @@ -1,39 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn __main() -> i32 { -block0: - v1 = const.i32 25 : i32; - v2 = call noname::fib(v1) : i32; - ret v2; -} - -pub fn fib(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 0 : i32; - v3 = const.i32 0 : i32; - v4 = const.i32 1 : i32; - br block2(v4, v0, v3); - -block1(v1: i32): - -block2(v6: i32, v7: i32, v9: i32): - v8 = neq v7, 0 : i1; - condbr v8, block4, block5; - -block3(v5: i32): - -block4: - v10 = const.i32 -1 : i32; - v11 = add.wrapping v7, v10 : i32; - v12 = add.wrapping v9, v6 : i32; - br block2(v12, v11, v6); - -block5: - ret v9; -} diff --git a/frontend-wasm/tests/expected/fib.wat b/frontend-wasm/tests/expected/fib.wat deleted file mode 100644 index 53ee81aca..000000000 --- a/frontend-wasm/tests/expected/fib.wat +++ /dev/null @@ -1,45 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (type (;1;) (func (result i32))) - (func $fib (;0;) (type 0) (param i32) (result i32) - (local i32 i32 i32) - i32.const 0 - local.set 1 - i32.const 1 - local.set 2 - loop (result i32) ;; label = @1 - local.get 2 - local.set 3 - block ;; label = @2 - local.get 0 - br_if 0 (;@2;) - local.get 1 - return - end - local.get 0 - i32.const -1 - i32.add - local.set 0 - local.get 1 - local.get 3 - i32.add - local.set 2 - local.get 3 - local.set 1 - br 0 (;@1;) - end - ) - (func $__main (;1;) (type 1) (result i32) - i32.const 25 - call $fib - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "fib" (func $fib)) - (export "__main" (func $__main)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/frontend-wasm/tests/expected/signed_arith.mir b/frontend-wasm/tests/expected/signed_arith.mir deleted file mode 100644 index 7f16896c2..000000000 --- a/frontend-wasm/tests/expected/signed_arith.mir +++ /dev/null @@ -1,386 +0,0 @@ -module noname - -memory { - segment @0x100000 x 336 = 0x00000002000000010000000000000001000000050000001e0000004800100000776f6c667265766f2068746977207265646e69616d657220656874206574616c75636c6163206f742074706d65747461000000000000006f72657a20666f20726f736976696420612068746977207265646e69616d657220656874206574616c75636c6163206f742074706d65747461000000050000001800000048001000000000000500000012000000480010000000776f6c667265766f206874697720656469766964206f742074706d65747461000000000000006f72657a20796220656469766964206f742074706d657474610000000000000000000000050000000c000000480010000073722e336634633134343734643266663337343732323933303434663363313262313536393738323535636666643934666666383264323264333438626430643363362f706d742f; -} - -global external __stack_pointer : i32 = 0x00100000 { id = gvar0 }; -global external gv1 : i32 = 0x00100150 { id = gvar1 }; -global external gv2 : i32 = 0x00100150 { id = gvar2 }; - - -pub fn rust_begin_unwind(i32) { -block0(v0: i32): -{ - br block2 -} - -block1: -{ -} - -block2: -{ - br block2 -} - -block3: -{ -} -} - -pub fn div_s(i32, i32) -> i32 { -block0(v0: i32, v1: i32): -{ - v3 = eq v1, 0 : i1 - v4 = cast v3 : i32 - v5 = neq v4, 0 : i1 - condbr v5, block3, block4 -} - -block1(v2: i32): -{ - ret (v2) -} - -block2: -{ - v22 = div v0, v1 : i32 - br block1(v22) -} - -block3: -{ - v17 = const.i32 1048672 : i32 - v18 = const.i32 25 : i32 - v19 = const.i32 1048648 : i32 - call noname::core::panicking::panic(v17, v18, v19) - unreachable -} - -block4: -{ - v6 = const.i32 -2147483648 : i32 - v7 = neq v0, v6 : i1 - v8 = cast v7 : i32 - v9 = neq v8, 0 : i1 - condbr v9, block2, block5 -} - -block5: -{ - v10 = const.i32 -1 : i32 - v11 = neq v1, v10 : i1 - v12 = cast v11 : i32 - v13 = neq v12, 0 : i1 - condbr v13, block2, block6 -} - -block6: -{ - v14 = const.i32 1048704 : i32 - v15 = const.i32 31 : i32 - v16 = const.i32 1048648 : i32 - call noname::core::panicking::panic(v14, v15, v16) - unreachable -} -} - -pub fn div_u(i32, i32) -> i32 { -block0(v0: i32, v1: i32): -{ - v3 = eq v1, 0 : i1 - v4 = cast v3 : i32 - v5 = neq v4, 0 : i1 - condbr v5, block2, block3 -} - -block1(v2: i32): -{ -} - -block2: -{ - v10 = const.i32 1048672 : i32 - v11 = const.i32 25 : i32 - v12 = const.i32 1048736 : i32 - call noname::core::panicking::panic(v10, v11, v12) - unreachable -} - -block3: -{ - v6 = cast v0 : u32 - v7 = cast v1 : u32 - v8 = div v6, v7 : u32 - v9 = cast v8 : i32 - ret (v9) -} -} - -pub fn rem_s(i32, i32) -> i32 { -block0(v0: i32, v1: i32): -{ - v3 = eq v1, 0 : i1 - v4 = cast v3 : i32 - v5 = neq v4, 0 : i1 - condbr v5, block3, block4 -} - -block1(v2: i32): -{ - ret (v2) -} - -block2: -{ - v22 = mod v0, v1 : i32 - br block1(v22) -} - -block3: -{ - v17 = const.i32 1048768 : i32 - v18 = const.i32 57 : i32 - v19 = const.i32 1048752 : i32 - call noname::core::panicking::panic(v17, v18, v19) - unreachable -} - -block4: -{ - v6 = const.i32 -2147483648 : i32 - v7 = neq v0, v6 : i1 - v8 = cast v7 : i32 - v9 = neq v8, 0 : i1 - condbr v9, block2, block5 -} - -block5: -{ - v10 = const.i32 -1 : i32 - v11 = neq v1, v10 : i1 - v12 = cast v11 : i32 - v13 = neq v12, 0 : i1 - condbr v13, block2, block6 -} - -block6: -{ - v14 = const.i32 1048832 : i32 - v15 = const.i32 48 : i32 - v16 = const.i32 1048752 : i32 - call noname::core::panicking::panic(v14, v15, v16) - unreachable -} -} - -pub fn rem_u(i32, i32) -> i32 { -block0(v0: i32, v1: i32): -{ - v3 = eq v1, 0 : i1 - v4 = cast v3 : i32 - v5 = neq v4, 0 : i1 - condbr v5, block2, block3 -} - -block1(v2: i32): -{ -} - -block2: -{ - v10 = const.i32 1048768 : i32 - v11 = const.i32 57 : i32 - v12 = const.i32 1048880 : i32 - call noname::core::panicking::panic(v10, v11, v12) - unreachable -} - -block3: -{ - v6 = cast v0 : u32 - v7 = cast v1 : u32 - v8 = mod v6, v7 : u32 - v9 = cast v8 : i32 - ret (v9) -} -} - -pub fn shr_s(i32, i32) -> i32 { -block0(v0: i32, v1: i32): -{ - v3 = shr v0, v1 : i32 - br block1(v3) -} - -block1(v2: i32): -{ - ret (v2) -} -} - -pub fn shr_u(i32, i32) -> i32 { -block0(v0: i32, v1: i32): -{ - v3 = cast v0 : u32 - v4 = cast v1 : u32 - v5 = shr v3, v4 : u32 - v6 = cast v5 : i32 - br block1(v6) -} - -block1(v2: i32): -{ - ret (v2) -} -} - -pub fn __main() -> i32 { -block0: -{ - v1 = const.i32 -8 : i32 - v2 = const.i32 -4 : i32 - v3 = call noname::div_s(v1, v2) : i32 - v4 = const.i32 -8 : i32 - v5 = const.i32 -3 : i32 - v6 = call noname::rem_s(v4, v5) : i32 - v7 = add v3, v6 : i32 - v8 = const.i32 -16 : i32 - v9 = const.i32 2 : i32 - v10 = call noname::shr_s(v8, v9) : i32 - v11 = add v7, v10 : i32 - v12 = const.i32 8 : i32 - v13 = const.i32 4 : i32 - v14 = call noname::div_u(v12, v13) : i32 - v15 = add v11, v14 : i32 - v16 = const.i32 8 : i32 - v17 = const.i32 3 : i32 - v18 = call noname::rem_u(v16, v17) : i32 - v19 = add v15, v18 : i32 - v20 = const.i32 16 : i32 - v21 = const.i32 2 : i32 - v22 = call noname::shr_u(v20, v21) : i32 - v23 = add v19, v22 : i32 - br block1(v23) -} - -block1(v0: i32): -{ - ret (v0) -} -} - -pub fn core::ptr::drop_in_place(i32) { -block0(v0: i32): -{ - br block1 -} - -block1: -{ - ret -} -} - -pub fn core::panicking::panic_fmt(i32, i32) { -block0(v0: i32, v1: i32): -{ - v2 = const.i32 0 : i32 - v3 = global.load (@__stack_pointer) as *mut i8 : i32 - v4 = const.i32 32 : i32 - v5 = sub v3, v4 : i32 - v6 = global.symbol @__stack_pointer : *mut i32 - store v6, v5 - v7 = const.i32 1 : i32 - v8 = trunc v7 : u16 - v9 = cast v5 : u32 - v10 = add v9, 28 : u32 - v11 = inttoptr v10 : *mut u16 - store v11, v8 - v12 = cast v5 : u32 - v13 = add v12, 24 : u32 - v14 = inttoptr v13 : *mut i32 - store v14, v1 - v15 = cast v5 : u32 - v16 = add v15, 20 : u32 - v17 = inttoptr v16 : *mut i32 - store v17, v0 - v18 = const.i32 1048896 : i32 - v19 = cast v5 : u32 - v20 = add v19, 16 : u32 - v21 = inttoptr v20 : *mut i32 - store v21, v18 - v22 = const.i32 1048896 : i32 - v23 = cast v5 : u32 - v24 = add v23, 12 : u32 - v25 = inttoptr v24 : *mut i32 - store v25, v22 - v26 = const.i32 12 : i32 - v27 = add v5, v26 : i32 - call noname::rust_begin_unwind(v27) - unreachable -} - -pub fn core::panicking::panic(i32, i32, i32) { -block0(v0: i32, v1: i32, v2: i32): -{ - v3 = const.i32 0 : i32 - v4 = global.load (@__stack_pointer) as *mut i8 : i32 - v5 = const.i32 32 : i32 - v6 = sub v4, v5 : i32 - v7 = global.symbol @__stack_pointer : *mut i32 - store v7, v6 - v8 = const.i32 12 : i32 - v9 = add v6, v8 : i32 - v10 = const.i64 0 : i64 - v11 = cast v9 : u32 - v12 = inttoptr v11 : *mut i64 - store v12, v10 - v13 = const.i32 1 : i32 - v14 = cast v6 : u32 - v15 = add v14, 4 : u32 - v16 = inttoptr v15 : *mut i32 - store v16, v13 - v17 = const.i32 1048896 : i32 - v18 = cast v6 : u32 - v19 = add v18, 8 : u32 - v20 = inttoptr v19 : *mut i32 - store v20, v17 - v21 = cast v6 : u32 - v22 = add v21, 28 : u32 - v23 = inttoptr v22 : *mut i32 - store v23, v1 - v24 = cast v6 : u32 - v25 = add v24, 24 : u32 - v26 = inttoptr v25 : *mut i32 - store v26, v0 - v27 = const.i32 24 : i32 - v28 = add v6, v27 : i32 - v29 = cast v6 : u32 - v30 = inttoptr v29 : *mut i32 - store v30, v28 - call noname::core::panicking::panic_fmt(v6, v2) - unreachable -} - -pub fn ::type_id(i32, i32) { -block0(v0: i32, v1: i32): -{ - v2 = const.i64 -8014429818408747214 : i64 - v3 = cast v0 : u32 - v4 = add v3, 8 : u32 - v5 = inttoptr v4 : *mut i64 - store v5, v2 - v6 = const.i64 167320651382453006 : i64 - v7 = cast v0 : u32 - v8 = inttoptr v7 : *mut i64 - store v8, v6 - br block1 -} - -block1: -{ - ret -} -} diff --git a/frontend-wasm/tests/expected/signed_arith.wat b/frontend-wasm/tests/expected/signed_arith.wat deleted file mode 100644 index 5f9222b05..000000000 --- a/frontend-wasm/tests/expected/signed_arith.wat +++ /dev/null @@ -1,227 +0,0 @@ -(module - (type (;0;) (func (param i32))) - (type (;1;) (func (param i32 i32) (result i32))) - (type (;2;) (func (result i32))) - (type (;3;) (func (param i32 i32))) - (type (;4;) (func (param i32 i32 i32))) - (func $rust_begin_unwind (;0;) (type 0) (param i32) - loop ;; label = @1 - br 0 (;@1;) - end - ) - (func $div_s (;1;) (type 1) (param i32 i32) (result i32) - block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.eqz - br_if 0 (;@2;) - local.get 0 - i32.const -2147483648 - i32.ne - br_if 1 (;@1;) - local.get 1 - i32.const -1 - i32.ne - br_if 1 (;@1;) - i32.const 1048704 - i32.const 31 - i32.const 1048648 - call $core::panicking::panic - unreachable - end - i32.const 1048672 - i32.const 25 - i32.const 1048648 - call $core::panicking::panic - unreachable - end - local.get 0 - local.get 1 - i32.div_s - ) - (func $div_u (;2;) (type 1) (param i32 i32) (result i32) - block ;; label = @1 - local.get 1 - i32.eqz - br_if 0 (;@1;) - local.get 0 - local.get 1 - i32.div_u - return - end - i32.const 1048672 - i32.const 25 - i32.const 1048736 - call $core::panicking::panic - unreachable - ) - (func $rem_s (;3;) (type 1) (param i32 i32) (result i32) - block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.eqz - br_if 0 (;@2;) - local.get 0 - i32.const -2147483648 - i32.ne - br_if 1 (;@1;) - local.get 1 - i32.const -1 - i32.ne - br_if 1 (;@1;) - i32.const 1048832 - i32.const 48 - i32.const 1048752 - call $core::panicking::panic - unreachable - end - i32.const 1048768 - i32.const 57 - i32.const 1048752 - call $core::panicking::panic - unreachable - end - local.get 0 - local.get 1 - i32.rem_s - ) - (func $rem_u (;4;) (type 1) (param i32 i32) (result i32) - block ;; label = @1 - local.get 1 - i32.eqz - br_if 0 (;@1;) - local.get 0 - local.get 1 - i32.rem_u - return - end - i32.const 1048768 - i32.const 57 - i32.const 1048880 - call $core::panicking::panic - unreachable - ) - (func $shr_s (;5;) (type 1) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.shr_s - ) - (func $shr_u (;6;) (type 1) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.shr_u - ) - (func $__main (;7;) (type 2) (result i32) - i32.const -8 - i32.const -4 - call $div_s - i32.const -8 - i32.const -3 - call $rem_s - i32.add - i32.const -16 - i32.const 2 - call $shr_s - i32.add - i32.const 8 - i32.const 4 - call $div_u - i32.add - i32.const 8 - i32.const 3 - call $rem_u - i32.add - i32.const 16 - i32.const 2 - call $shr_u - i32.add - ) - (func $core::ptr::drop_in_place (;8;) (type 0) (param i32)) - (func $core::panicking::panic_fmt (;9;) (type 3) (param i32 i32) - (local i32) - global.get $__stack_pointer - i32.const 32 - i32.sub - local.tee 2 - global.set $__stack_pointer - local.get 2 - i32.const 1 - i32.store16 offset=28 - local.get 2 - local.get 1 - i32.store offset=24 - local.get 2 - local.get 0 - i32.store offset=20 - local.get 2 - i32.const 1048896 - i32.store offset=16 - local.get 2 - i32.const 1048896 - i32.store offset=12 - local.get 2 - i32.const 12 - i32.add - call $rust_begin_unwind - unreachable - ) - (func $core::panicking::panic (;10;) (type 4) (param i32 i32 i32) - (local i32) - global.get $__stack_pointer - i32.const 32 - i32.sub - local.tee 3 - global.set $__stack_pointer - local.get 3 - i32.const 12 - i32.add - i64.const 0 - i64.store align=4 - local.get 3 - i32.const 1 - i32.store offset=4 - local.get 3 - i32.const 1048896 - i32.store offset=8 - local.get 3 - local.get 1 - i32.store offset=28 - local.get 3 - local.get 0 - i32.store offset=24 - local.get 3 - local.get 3 - i32.const 24 - i32.add - i32.store - local.get 3 - local.get 2 - call $core::panicking::panic_fmt - unreachable - ) - (func $::type_id (;11;) (type 3) (param i32 i32) - local.get 0 - i64.const -8014429818408747214 - i64.store offset=8 - local.get 0 - i64.const 167320651382453006 - i64.store - ) - (table (;0;) 3 3 funcref) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048912) - (global (;2;) i32 i32.const 1048912) - (export "memory" (memory 0)) - (export "div_s" (func $div_s)) - (export "div_u" (func $div_u)) - (export "rem_s" (func $rem_s)) - (export "rem_u" (func $rem_u)) - (export "shr_s" (func $shr_s)) - (export "shr_u" (func $shr_u)) - (export "__main" (func $__main)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) - (elem (;0;) (i32.const 1) func $core::ptr::drop_in_place $::type_id) - (data $.rodata (;0;) (i32.const 1048576) "/tmp/6c3d0db843d22d28fff49dffc552879651b21c3f44039227473ff2d47441c4f3.rs\00\00\10\00H\00\00\00\0c\00\00\00\05\00\00\00\00\00\00\00\00\00\00\00attempt to divide by zero\00\00\00\00\00\00\00attempt to divide with overflow\00\00\00\10\00H\00\00\00\12\00\00\00\05\00\00\00\00\00\10\00H\00\00\00\18\00\00\00\05\00\00\00attempt to calculate the remainder with a divisor of zero\00\00\00\00\00\00\00attempt to calculate the remainder with overflow\00\00\10\00H\00\00\00\1e\00\00\00\05\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\02\00\00\00") -) \ No newline at end of file diff --git a/frontend-wasm/tests/expected/static_mut.hir b/frontend-wasm/tests/expected/static_mut.hir deleted file mode 100644 index cd2e84063..000000000 --- a/frontend-wasm/tests/expected/static_mut.hir +++ /dev/null @@ -1,58 +0,0 @@ -module noname - -const $0 = 0x00100000; -const $1 = 0x00100009; -const $2 = 0x00100010; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $1 { id = 1 }; -global external @gv2 : i32 = $2 { id = 2 }; - -pub fn __main() -> i32 { -block0: - v1 = const.i32 0 : i32; - call noname::global_var_update(); - v2 = const.i32 0 : i32; - v3 = const.i32 -9 : i32; - br block2(v3, v2); - -block2(v4: i32, v11: i32): - v5 = const.i32 1048585 : i32; - v6 = add.wrapping v4, v5 : i32; - v7 = cast v6 : u32; - v8 = inttoptr v7 : *mut u8; - v9 = load v8 : u8; - v10 = zext v9 : i32; - v12 = add.wrapping v10, v11 : i32; - v13 = const.i32 1 : i32; - v14 = add.wrapping v4, v13 : i32; - v15 = neq v14, 0 : i1; - condbr v15, block5, block4; - -block5: - br block2(v14, v12); - -block4: - v16 = const.i32 255 : i32; - v17 = band v12, v16 : i32; - ret v17; -} - -pub fn global_var_update() { -block0: - v0 = const.i32 0 : i32; - v1 = const.i32 0 : i32; - v2 = cast v1 : u32; - v3 = add.checked v2, 1048577 : u32; - v4 = inttoptr v3 : *mut u8; - v5 = load v4 : u8; - v6 = zext v5 : i32; - v7 = const.i32 1 : i32; - v8 = add.wrapping v6, v7 : i32; - v9 = trunc v8 : u8; - v10 = cast v0 : u32; - v11 = add.checked v10, 1048576 : u32; - v12 = inttoptr v11 : *mut u8; - store v12, v9; - ret; -} diff --git a/frontend-wasm/tests/expected/static_mut.wat b/frontend-wasm/tests/expected/static_mut.wat deleted file mode 100644 index 9f46a09ad..000000000 --- a/frontend-wasm/tests/expected/static_mut.wat +++ /dev/null @@ -1,49 +0,0 @@ -(module - (type (;0;) (func)) - (type (;1;) (func (result i32))) - (func $global_var_update (;0;) (type 0) - i32.const 0 - i32.const 0 - i32.load8_u offset=1048577 - i32.const 1 - i32.add - i32.store8 offset=1048576 - ) - (func $__main (;1;) (type 1) (result i32) - (local i32 i32 i32) - call $global_var_update - i32.const 0 - local.set 0 - i32.const -9 - local.set 1 - loop ;; label = @1 - local.get 1 - i32.const 1048585 - i32.add - i32.load8_u - local.get 0 - i32.add - local.set 0 - local.get 1 - i32.const 1 - i32.add - local.tee 2 - local.set 1 - local.get 2 - br_if 0 (;@1;) - end - local.get 0 - i32.const 255 - i32.and - ) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048585) - (global (;2;) i32 i32.const 1048592) - (export "memory" (memory 0)) - (export "global_var_update" (func $global_var_update)) - (export "__main" (func $__main)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) - (data $.data (;0;) (i32.const 1048576) "\01\02\03\04\05\06\07\08\09") -) \ No newline at end of file diff --git a/frontend-wasm/tests/rust_source/add.rs b/frontend-wasm/tests/rust_source/add.rs deleted file mode 100644 index ea10f38ee..000000000 --- a/frontend-wasm/tests/rust_source/add.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![no_std] -#![no_main] - -#[panic_handler] -fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[inline(never)] -#[no_mangle] -fn add(a: i32, b: i32) -> i32 { - a + b -} - -#[no_mangle] -pub extern "C" fn __main() -> i32 { - let a = 1; - let b = 2; - add(a, b) -} diff --git a/frontend-wasm/tests/rust_source/array.rs b/frontend-wasm/tests/rust_source/array.rs deleted file mode 100644 index 8dab20c15..000000000 --- a/frontend-wasm/tests/rust_source/array.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![no_std] -#![no_main] - -#[panic_handler] -fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[inline(never)] -#[no_mangle] -pub fn sum_arr(arr: &[u32]) -> u32 { - arr.iter().sum() -} - -#[no_mangle] -pub extern "C" fn __main() -> u32 { - sum_arr(&[1, 2, 3, 4, 5]) + sum_arr(&[6, 7, 8, 9, 10]) -} diff --git a/frontend-wasm/tests/rust_source/enum.rs b/frontend-wasm/tests/rust_source/enum.rs deleted file mode 100644 index 566f493fa..000000000 --- a/frontend-wasm/tests/rust_source/enum.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![no_main] - -#[panic_handler] -fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -enum Op { - Add, - Sub, - Mul, -} - -#[inline(never)] -#[no_mangle] -fn match_enum(a: u32, b: u32, foo: Op) -> u32 { - match foo { - Op::Add => a + b, - Op::Sub => a - b, - Op::Mul => a * b, - } -} - -#[no_mangle] -pub extern "C" fn __main() -> u32 { - match_enum(3, 5, Op::Add) + match_enum(3, 5, Op::Sub) + match_enum(3, 5, Op::Mul) -} diff --git a/frontend-wasm/tests/rust_source/fib.rs b/frontend-wasm/tests/rust_source/fib.rs deleted file mode 100644 index 8c7b9b987..000000000 --- a/frontend-wasm/tests/rust_source/fib.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![no_std] -#![no_main] - -#[panic_handler] -fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[inline(never)] -#[no_mangle] -pub fn fib(n: u32) -> u32 { - let mut a = 0; - let mut b = 1; - for _ in 0..n { - let c = a + b; - a = b; - b = c; - } - a -} - -#[no_mangle] -pub extern "C" fn __main() -> u32 { - fib(25) -} diff --git a/frontend-wasm/tests/rust_source/signed_arith.rs b/frontend-wasm/tests/rust_source/signed_arith.rs deleted file mode 100644 index 14c4330e5..000000000 --- a/frontend-wasm/tests/rust_source/signed_arith.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![no_std] -#![no_main] - -#[panic_handler] -fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[inline(never)] -#[no_mangle] -fn div_s(a: i32, b: i32) -> i32 { - a / b -} - -#[inline(never)] -#[no_mangle] -fn div_u(a: u32, b: u32) -> u32 { - a / b -} - -#[inline(never)] -#[no_mangle] -fn rem_s(a: i32, b: i32) -> i32 { - a % b -} - -#[inline(never)] -#[no_mangle] -fn rem_u(a: u32, b: u32) -> u32 { - a % b -} - -#[inline(never)] -#[no_mangle] -fn shr_s(a: i32, b: i32) -> i32 { - a >> b -} - -#[inline(never)] -#[no_mangle] -fn shr_u(a: u32, b: u32) -> u32 { - a >> b -} - -#[no_mangle] -pub extern "C" fn __main() -> i32 { - div_s(-8, -4) + rem_s(-8, -3) + shr_s(-16, 2) - + (div_u(8, 4) + rem_u(8, 3) + shr_u(16, 2)) as i32 -} diff --git a/frontend-wasm/tests/rust_source/static_mut.rs b/frontend-wasm/tests/rust_source/static_mut.rs deleted file mode 100644 index 2d82148f2..000000000 --- a/frontend-wasm/tests/rust_source/static_mut.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![no_std] -#![no_main] - -#[panic_handler] -fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -static mut G1: [u8; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - -#[inline(never)] -#[no_mangle] -fn global_var_update() { - unsafe { - G1[0] = G1[1] + 1; - } -} - -#[no_mangle] -pub extern "C" fn __main() -> u32 { - global_var_update(); - unsafe { G1.into_iter().sum::() as u32 } -} diff --git a/frontend-wasm/tests/test_rust_comp.rs b/frontend-wasm/tests/test_rust_comp.rs deleted file mode 100644 index cfb5b6b96..000000000 --- a/frontend-wasm/tests/test_rust_comp.rs +++ /dev/null @@ -1,45 +0,0 @@ -use expect_test::expect_file; -use miden_integration_tests::CompilerTest; - -#[test] -fn rust_add() { - let mut test = CompilerTest::rust_source_program(include_str!("rust_source/add.rs")); - test.expect_wasm(expect_file!["./expected/add.wat"]); - test.expect_ir(expect_file!["./expected/add.hir"]); -} - -#[test] -fn rust_fib() { - let mut test = CompilerTest::rust_source_program(include_str!("rust_source/fib.rs")); - test.expect_wasm(expect_file!["./expected/fib.wat"]); - test.expect_ir(expect_file!["./expected/fib.hir"]); -} - -#[test] -fn rust_enum() { - let mut test = CompilerTest::rust_source_program(include_str!("rust_source/enum.rs")); - test.expect_wasm(expect_file!["./expected/enum.wat"]); - test.expect_ir(expect_file!["./expected/enum.hir"]); -} - -#[test] -fn rust_array() { - let mut test = CompilerTest::rust_source_program(include_str!("rust_source/array.rs")); - test.expect_wasm(expect_file!["./expected/array.wat"]); - test.expect_ir(expect_file!["./expected/array.hir"]); - assert!( - test.hir.unwrap().segments().last().unwrap().is_readonly(), - "data segment should be readonly" - ); -} - -#[test] -fn rust_static_mut() { - let mut test = CompilerTest::rust_source_program(include_str!("rust_source/static_mut.rs")); - test.expect_wasm(expect_file!["./expected/static_mut.wat"]); - test.expect_ir(expect_file!["./expected/static_mut.hir"]); - assert!( - !test.hir.unwrap().segments().last().unwrap().is_readonly(), - "data segment should be mutable" - ); -} diff --git a/hir-analysis/Cargo.toml b/hir-analysis/Cargo.toml deleted file mode 100644 index 257a3b9b5..000000000 --- a/hir-analysis/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "miden-hir-analysis" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -anyhow.workspace = true -cranelift-entity.workspace = true -cranelift-bforest.workspace = true -inventory.workspace = true -miden-diagnostics.workspace = true -miden-hir.workspace = true -midenc-session.workspace = true -rustc-hash.workspace = true -smallvec.workspace = true -thiserror.workspace = true diff --git a/hir-analysis/src/control_flow.rs b/hir-analysis/src/control_flow.rs deleted file mode 100644 index 5c34296b9..000000000 --- a/hir-analysis/src/control_flow.rs +++ /dev/null @@ -1,423 +0,0 @@ -use cranelift_bforest as bforest; -use cranelift_entity::SecondaryMap; - -use miden_hir::pass::{Analysis, AnalysisManager, AnalysisResult}; -use miden_hir::{Block, DataFlowGraph, Function, Inst, Instruction}; -use midenc_session::Session; - -/// Represents the predecessor of the current basic block. -/// -/// A predecessor in this context is both the instruction which transfers control to -/// the current block, and the block which encloses that instruction. -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub struct BlockPredecessor { - pub block: Block, - pub inst: Inst, -} -impl BlockPredecessor { - #[inline] - pub fn new(block: Block, inst: Inst) -> Self { - Self { block, inst } - } -} - -/// A node in the control flow graph, which contains the successors and predecessors of a given `Block`. -#[derive(Clone, Default)] -struct Node { - /// Instructions which transfer control to this block - pub predecessors: bforest::Map, - /// Set of blocks that are targets of branches/jumps in this block. - pub successors: bforest::Set, -} - -/// The control flow graph maps all blocks in a function to their predecessor and successor blocks. -pub struct ControlFlowGraph { - data: SecondaryMap, - pred_forest: bforest::MapForest, - succ_forest: bforest::SetForest, - valid: bool, -} -impl Clone for ControlFlowGraph { - fn clone(&self) -> Self { - let mut data = SecondaryMap::::with_capacity(self.data.capacity()); - let mut pred_forest = bforest::MapForest::new(); - let mut succ_forest = bforest::SetForest::new(); - - for (k, v) in self.data.iter() { - let node = &mut data[k]; - for (pk, pv) in v.predecessors.iter(&self.pred_forest) { - node.predecessors.insert(pk, pv, &mut pred_forest, &()); - } - for succ in v.successors.iter(&self.succ_forest) { - node.successors.insert(succ, &mut succ_forest, &()); - } - } - - Self { - data, - pred_forest, - succ_forest, - valid: self.valid, - } - } -} -impl Default for ControlFlowGraph { - fn default() -> Self { - Self { - data: SecondaryMap::default(), - pred_forest: bforest::MapForest::new(), - succ_forest: bforest::SetForest::new(), - valid: false, - } - } -} -impl Analysis for ControlFlowGraph { - type Entity = Function; - - fn analyze( - function: &Self::Entity, - _analyses: &mut AnalysisManager, - _session: &Session, - ) -> AnalysisResult { - Ok(ControlFlowGraph::with_function(function)) - } -} -impl ControlFlowGraph { - pub fn new() -> Self { - Self::default() - } - - /// Reset this control flow graph to its initial state for reuse - pub fn clear(&mut self) { - self.data.clear(); - self.pred_forest.clear(); - self.succ_forest.clear(); - self.valid = false; - } - - /// Obtain a control flow graph computed over `func`. - pub fn with_function(func: &Function) -> Self { - let mut cfg = Self::new(); - cfg.compute(&func.dfg); - cfg - } - - /// Compute the control flow graph for `dfg`. - /// - /// NOTE: This will reset the current state of this graph. - pub fn compute(&mut self, dfg: &DataFlowGraph) { - self.clear(); - self.data.resize(dfg.num_blocks()); - - for (block, _) in dfg.blocks() { - self.compute_block(dfg, block); - } - - self.valid = true; - } - - /// Recompute the control flow graph of `block`. - /// - /// This is for use after modifying instructions within a block. It recomputes all edges - /// from `block` while leaving edges to `block` intact. It performs a restricted version of - /// `compute` which allows us to avoid recomputing the graph for all blocks, only those which - /// are modified by a specific set of changes. - pub fn recompute_block(&mut self, dfg: &DataFlowGraph, block: Block) { - debug_assert!(self.is_valid()); - self.invalidate_block_successors(block); - self.compute_block(dfg, block); - } - - /// Similar to `recompute_block`, this recomputes all edges from `block` as if they had been - /// removed, while leaving edges to `block` intact. It is expected that predecessor blocks - /// will have `recompute_block` subsequently called on them so that `block` is fully removed - /// from the CFG. - pub fn detach_block(&mut self, block: Block) { - debug_assert!(self.is_valid()); - self.invalidate_block_successors(block); - } - - /// Return the number of predecessors for `block` - pub fn num_predecessors(&self, block: Block) -> usize { - self.data[block] - .predecessors - .iter(&self.pred_forest) - .count() - } - - /// Return the number of successors for `block` - pub fn num_successors(&self, block: Block) -> usize { - self.data[block].successors.iter(&self.succ_forest).count() - } - - /// Get an iterator over the CFG predecessors to `block`. - pub fn pred_iter(&self, block: Block) -> PredIter { - PredIter(self.data[block].predecessors.iter(&self.pred_forest)) - } - - /// Get an iterator over the CFG successors to `block`. - pub fn succ_iter(&self, block: Block) -> SuccIter { - debug_assert!(self.is_valid()); - self.data[block].successors.iter(&self.succ_forest) - } - - /// Check if the CFG is in a valid state. - /// - /// Note that this doesn't perform any kind of validity checks. It simply checks if the - /// `compute()` method has been called since the last `clear()`. It does not check that the - /// CFG is consistent with the function. - pub fn is_valid(&self) -> bool { - self.valid - } - - fn compute_block(&mut self, dfg: &DataFlowGraph, block: Block) { - visit_block_succs(dfg, block, |inst, dest, _| { - self.add_edge(block, inst, dest); - }); - } - - fn invalidate_block_successors(&mut self, block: Block) { - use core::mem; - - let mut successors = mem::take(&mut self.data[block].successors); - for succ in successors.iter(&self.succ_forest) { - self.data[succ] - .predecessors - .retain(&mut self.pred_forest, |_, &mut e| e != block); - } - successors.clear(&mut self.succ_forest); - } - - fn add_edge(&mut self, from: Block, from_inst: Inst, to: Block) { - self.data[from] - .successors - .insert(to, &mut self.succ_forest, &()); - self.data[to] - .predecessors - .insert(from_inst, from, &mut self.pred_forest, &()); - } -} - -/// An iterator over block predecessors. The iterator type is `BlockPredecessor`. -/// -/// Each predecessor is an instruction that branches to the block. -pub struct PredIter<'a>(bforest::MapIter<'a, Inst, Block>); - -impl<'a> Iterator for PredIter<'a> { - type Item = BlockPredecessor; - - fn next(&mut self) -> Option { - self.0.next().map(|(i, e)| BlockPredecessor::new(e, i)) - } -} - -/// An iterator over block successors. The iterator type is `Block`. -pub type SuccIter<'a> = bforest::SetIter<'a, Block>; - -/// Visit all successors of a block with a given visitor closure. The closure -/// arguments are the branch instruction that is used to reach the successor, -/// the successor block itself, and a flag indicating whether the block is -/// branched to via a table entry. -pub(crate) fn visit_block_succs( - dfg: &DataFlowGraph, - block: Block, - mut visit: F, -) { - use miden_hir::{Br, CondBr, Switch}; - - if let Some(inst) = dfg.last_inst(block) { - match &dfg[inst] { - Instruction::Br(Br { - destination: dest, .. - }) => { - visit(inst, *dest, false); - } - - Instruction::CondBr(CondBr { - then_dest: (block_then, _), - else_dest: (block_else, _), - .. - }) => { - visit(inst, *block_then, false); - visit(inst, *block_else, false); - } - - Instruction::Switch(Switch { - ref arms, - default: default_block, - .. - }) => { - visit(inst, *default_block, false); - - for (_, dest) in arms.as_slice() { - visit(inst, *dest, true); - } - } - - inst => debug_assert!(!inst.opcode().is_branch()), - } - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - - use miden_diagnostics::{ - term::termcolor::ColorChoice, CodeMap, DefaultEmitter, DiagnosticsHandler, SourceSpan, - }; - use miden_hir::*; - - use super::*; - - #[test] - fn cfg_empty() { - let dfg = DataFlowGraph::default(); - - let mut cfg = ControlFlowGraph::new(); - cfg.compute(&dfg); - } - - #[test] - fn cfg_no_predecessors() { - let mut dfg = DataFlowGraph::default(); - - let _block0 = dfg.create_block(); - let _block1 = dfg.create_block(); - let _block2 = dfg.create_block(); - - let mut cfg = ControlFlowGraph::new(); - cfg.compute(&dfg); - - let mut blocks = dfg.blocks().map(|(blk, _)| blk); - for block in dfg.blocks().map(|(blk, _)| blk) { - assert_eq!(block, blocks.next().unwrap()); - assert_eq!(cfg.pred_iter(block).count(), 0); - assert_eq!(cfg.succ_iter(block).count(), 0); - } - } - - #[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); - - // Define the 'test' module - let mut builder = ModuleBuilder::new("test"); - - // Declare the `fib` function, with the appropriate type signature - let sig = Signature { - params: vec![AbiParam::new(Type::I32)], - results: vec![AbiParam::new(Type::I32)], - cc: CallConv::SystemV, - linkage: Linkage::External, - }; - let mut fb = builder - .function("branches_and_jumps", sig) - .expect("unexpected symbol conflict"); - - let block0 = fb.entry_block(); - let cond = { - let args = fb.block_params(block0); - args[0] - }; - - let block1 = fb.create_block(); - let block2 = fb.create_block(); - - let cond = fb.ins().trunc(cond, Type::I1, SourceSpan::default()); - let br_block0_block2_block1 = - fb.ins() - .cond_br(cond, block2, &[], block1, &[], SourceSpan::default()); - fb.switch_to_block(block1); - let br_block1_block1_block2 = - fb.ins() - .cond_br(cond, block1, &[], block2, &[], SourceSpan::default()); - - let id = fb - .build(&diagnostics) - .expect("unexpected validation error, see diagnostics output"); - - let mut module = builder.build(); - let mut function = module.unlink(id.function); - - let mut cfg = ControlFlowGraph::with_function(&function); - - { - let block0_predecessors = cfg.pred_iter(block0).collect::>(); - let block1_predecessors = cfg.pred_iter(block1).collect::>(); - let block2_predecessors = cfg.pred_iter(block2).collect::>(); - - let block0_successors = cfg.succ_iter(block0).collect::>(); - let block1_successors = cfg.succ_iter(block1).collect::>(); - let block2_successors = cfg.succ_iter(block2).collect::>(); - - assert_eq!(block0_predecessors.len(), 0); - assert_eq!(block1_predecessors.len(), 2); - assert_eq!(block2_predecessors.len(), 2); - - assert!(block1_predecessors - .contains(&BlockPredecessor::new(block0, br_block0_block2_block1))); - assert!(block1_predecessors - .contains(&BlockPredecessor::new(block1, br_block1_block1_block2))); - assert!(block2_predecessors - .contains(&BlockPredecessor::new(block0, br_block0_block2_block1))); - assert!(block2_predecessors - .contains(&BlockPredecessor::new(block1, br_block1_block1_block2))); - - assert_eq!(block0_successors, [block1, block2]); - assert_eq!(block1_successors, [block1, block2]); - assert_eq!(block2_successors, []); - } - - // Add a new block to hold a return instruction - let ret_block; - { - let mut builder = FunctionBuilder::new(&mut function); - ret_block = builder.create_block(); - builder.switch_to_block(ret_block); - builder.ins().ret(None, SourceSpan::default()); - } - - // Change some instructions and recompute block0 and ret_block - function.dfg.replace(br_block0_block2_block1).cond_br( - cond, - block1, - &[], - ret_block, - &[], - SourceSpan::default(), - ); - cfg.recompute_block(&function.dfg, block0); - cfg.recompute_block(&function.dfg, ret_block); - let br_block0_block1_ret_block = br_block0_block2_block1; - - { - let block0_predecessors = cfg.pred_iter(block0).collect::>(); - let block1_predecessors = cfg.pred_iter(block1).collect::>(); - let block2_predecessors = cfg.pred_iter(block2).collect::>(); - - let block0_successors = cfg.succ_iter(block0); - let block1_successors = cfg.succ_iter(block1); - let block2_successors = cfg.succ_iter(block2); - - assert_eq!(block0_predecessors.len(), 0); - assert_eq!(block1_predecessors.len(), 2); - assert_eq!(block2_predecessors.len(), 1); - - assert!(block1_predecessors - .contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),); - assert!(block1_predecessors - .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),); - assert!(!block2_predecessors - .contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),); - assert!(block2_predecessors - .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),); - - assert_eq!(block0_successors.collect::>(), [block1, ret_block]); - assert_eq!(block1_successors.collect::>(), [block1, block2]); - assert_eq!(block2_successors.collect::>(), []); - } - } -} diff --git a/hir-analysis/src/data.rs b/hir-analysis/src/data.rs deleted file mode 100644 index f728aff16..000000000 --- a/hir-analysis/src/data.rs +++ /dev/null @@ -1,149 +0,0 @@ -use miden_hir::pass::{Analysis, AnalysisManager, AnalysisResult}; -use miden_hir::{ - Function, FunctionIdent, GlobalValue, GlobalValueData, GlobalVariableTable, Module, Program, -}; -use midenc_session::Session; -use rustc_hash::FxHashMap; - -/// This analysis calculates the addresses/offsets of all global variables in a [Program] or [Module] -pub struct GlobalVariableAnalysis { - layout: GlobalVariableLayout, - _marker: core::marker::PhantomData, -} -impl Default for GlobalVariableAnalysis { - fn default() -> Self { - Self { - layout: Default::default(), - _marker: core::marker::PhantomData, - } - } -} -impl GlobalVariableAnalysis { - pub fn layout(&self) -> &GlobalVariableLayout { - &self.layout - } -} - -impl Analysis for GlobalVariableAnalysis { - type Entity = Program; - - fn analyze( - program: &Self::Entity, - _analyses: &mut AnalysisManager, - _session: &Session, - ) -> AnalysisResult { - let mut layout = GlobalVariableLayout { - global_table_offset: program.segments().next_available_offset(), - ..GlobalVariableLayout::default() - }; - - let globals = program.globals(); - for module in program.modules().iter() { - for function in module.functions() { - let mut function_offsets = FxHashMap::default(); - for gv in function.dfg.globals.keys() { - if let Some(addr) = - compute_global_value_addr(gv, layout.global_table_offset, function, globals) - { - function_offsets.insert(gv, addr); - } - } - layout.offsets.insert(function.id, function_offsets); - } - } - - Ok(Self { - layout, - _marker: core::marker::PhantomData, - }) - } -} - -impl Analysis for GlobalVariableAnalysis { - type Entity = Module; - - fn analyze( - module: &Self::Entity, - _analyses: &mut AnalysisManager, - _session: &Session, - ) -> AnalysisResult { - let mut layout = GlobalVariableLayout { - global_table_offset: module.segments().next_available_offset(), - ..GlobalVariableLayout::default() - }; - - let globals = module.globals(); - for function in module.functions() { - let mut function_offsets = FxHashMap::default(); - for gv in function.dfg.globals.keys() { - if let Some(addr) = - compute_global_value_addr(gv, layout.global_table_offset, function, globals) - { - function_offsets.insert(gv, addr); - } - } - layout.offsets.insert(function.id, function_offsets); - } - - Ok(Self { - layout, - _marker: core::marker::PhantomData, - }) - } -} - -/// This struct contains data about the layout of global variables in linear memory -#[derive(Default, Clone)] -pub struct GlobalVariableLayout { - global_table_offset: u32, - offsets: FxHashMap>, -} -impl GlobalVariableLayout { - /// Get the address/offset at which global variables will start being allocated - pub fn global_table_offset(&self) -> u32 { - self.global_table_offset - } - - /// Get the statically-allocated address at which the global value `gv` for `function` is stored. - /// - /// This function returns `None` if the analysis does not know about `function`, `gv`, or if - /// the symbol which `gv` resolves to was undefined. - pub fn get_computed_addr(&self, function: &FunctionIdent, gv: GlobalValue) -> Option { - self.offsets - .get(function) - .and_then(|offsets| offsets.get(&gv).copied()) - } -} - -/// Computes the absolute offset (address) represented by the given global value -fn compute_global_value_addr( - mut gv: GlobalValue, - global_table_offset: u32, - function: &Function, - globals: &GlobalVariableTable, -) -> Option { - let mut relative_offset = 0; - loop { - let gv_data = function.dfg.global_value(gv); - relative_offset += gv_data.offset(); - match gv_data { - GlobalValueData::Symbol { name, .. } => { - let var = globals.find(*name)?; - let base_offset = unsafe { globals.offset_of(var) }; - if relative_offset >= 0 { - return Some((global_table_offset + base_offset) + relative_offset as u32); - } else { - return Some( - (global_table_offset + base_offset) - relative_offset.unsigned_abs(), - ); - } - } - GlobalValueData::IAddImm { base, .. } => { - gv = *base; - } - GlobalValueData::Load { base, .. } => { - gv = *base; - } - } - } -} diff --git a/hir-analysis/src/dependency_graph.rs b/hir-analysis/src/dependency_graph.rs deleted file mode 100644 index 3d4a97095..000000000 --- a/hir-analysis/src/dependency_graph.rs +++ /dev/null @@ -1,1501 +0,0 @@ -use std::cmp::Ordering; -use std::collections::{BTreeMap, BTreeSet}; -use std::fmt; - -use smallvec::SmallVec; - -use miden_hir as hir; - -/// This represents a node in a [DependencyGraph]. -/// -/// The node types here are carefully chosen to provide us with the following -/// properties once we've constructed a [DependencyGraph] from a block: -/// -/// * Distinguish between block-local operands and those which come from a -/// dominating block. This let's us reason globally about how function -/// arguments and instruction results are used in blocks of the program -/// so that they can be moved/copied as appropriate to keep them live only -/// for as long as they are needed. -/// * Represent the dependencies of individual arguments, this ensures -/// that dependencies between expressions in a block are correctly -/// represented when we compute a [TreeGraph], and that we can determine -/// exactly how many instances of a value are needed in a function. -/// * Represent usage of individual instruction results - both to ensure -/// we make copies of those results as needed, but to ensure we drop -/// unused results immediately if they are not needed. -/// -/// Furthermore, the precise layout and ordering of this enum is intentional, -/// as it determines the order in which nodes are sorted, and thus the order -/// in which we visit them during certain operations. -/// -/// It is also essential that this is kept in sync with [NodeId], which is -/// a packed representation of [Node] designed to ensure that the order in -/// which [NodeId] is ordered is the same as the corresponding [Node]. Put -/// another way: [Node] is the unpacked form of [NodeId]. -/// -/// NOTE: Adding variants/fields to this type must be done carefully, to ensure -/// that we can encode a [Node] as a [NodeId], and to preserve the fact that -/// a [NodeId] fits in a `u64`. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Node { - /// This node type represents a value known to be on the - /// operand stack upon entry to the current block, i.e. - /// it's definition is external to this block, but available. - Stack(hir::Value), - /// This node represents an instruction argument. Only `Inst` may - /// depend on nodes of this type directly, and it may only depend - /// on `Result` or `Stack` nodes itself. - /// - /// There are different kinds of arguments though, see [ArgumentNode] for details - Argument(ArgumentNode), - /// This node acts as a join point for the remaining node types, - /// i.e. it is the predecessor for `Argument`, and the successor - /// for `Result` and is used to represent the fact that results - /// implicitly depend on all arguments to the instruction which - /// produced them. - Inst { - /// The unique id of this instruction - id: hir::Inst, - /// The position of this instruction in its containing block - pos: u16, - }, - /// This node represents an instruction result. `Result` may only have - /// `Argument` as predecessor (i.e. the argument depends on a result), - /// and may only have `Inst` as successor (i.e. the instruction which - /// produced the result is the only way a result can appear in the graph). - Result { - /// The id of the value represented by this result - value: hir::Value, - /// The index of this result in the instruction results list - index: u8, - }, -} -impl core::hash::Hash for Node { - fn hash(&self, hasher: &mut H) { - // Ensure that by hashing either NodeId or Node we get the same hash - self.id().hash(hasher); - } -} -impl Node { - /// Get the identifier corresponding to this node. - /// - /// A given [Node] will always have the same identifier, as [NodeId] is - /// derived from the content of a [Node] (it is in fact a packed representation - /// of the same data). - pub fn id(self) -> NodeId { - NodeId::from(self) - } - - /// Returns true if this node represents an item in the current block - /// - /// The only node type for which this returns false is `Stack`, as such - /// values are by definition not defined in the current block. - #[inline] - pub fn is_block_local(&self) -> bool { - !matches!(self, Self::Stack(_)) - } - - /// Fallibly converts this node to an instruction identifier - #[inline] - pub fn as_instruction(&self) -> Option { - match self { - Self::Inst { id, .. } => Some(*id), - Self::Argument(ref arg) => Some(arg.inst()), - _ => None, - } - } - - /// Unwraps this node as an instruction identifier, or panics - pub fn unwrap_inst(&self) -> hir::Inst { - match self { - Self::Inst { id, .. } => *id, - Self::Argument(ref arg) => arg.inst(), - node => panic!("cannot unwrap node as instruction: {node:?}"), - } - } - - /// Fallibly converts this node to a value identifier - #[inline] - pub fn as_value(&self) -> Option { - match self { - Self::Stack(value) | Self::Result { value, .. } => Some(*value), - _ => None, - } - } -} -impl From for Node { - fn from(id: NodeId) -> Self { - id.into_node() - .unwrap_or_else(|_| panic!("invalid tag for node id: {:064b}", id.0)) - } -} -impl fmt::Debug for Node { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} -impl fmt::Display for Node { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Stack(value) => write!(f, "{value}"), - Self::Inst { id, .. } => write!(f, "{id}"), - Self::Argument(ref arg) => write!(f, "{arg:?}"), - Self::Result { value, .. } => write!(f, "result({value})"), - } - } -} - -/// This is a subtype of [Node] which represents the various types of arguments -/// we want to represent in a [DependencyGraph]. -/// -/// As with [Node], the layout and representation of this type is carefully -/// chosen, and must be kept in sync with [Node] and [NodeId]. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[repr(u8)] -pub enum ArgumentNode { - /// The argument is required by an instruction directly. - /// - /// For control-flow instructions, this argument type is used for - /// non-block arguments, e.g. in `cond_br v0, block1(v1)`, `v0` - /// would be of this type. - Direct { - /// The instruction to which this argument belongs - inst: hir::Inst, - /// The index of this argument in the instruction parameter list - index: u8, - }, - /// The argument is required by an instruction indirectly. - /// - /// This is only applicable to control-flow instructions, and indicates - /// that the argument is required along all control flow edges for which - /// the instruction is a predecessor. Each use of a value will get its - /// own node in the dependency graph to represent the specific position - /// of the argument in its respective block argument list. - /// - /// In the IR of `cond_br v0, block1(v1), block2(v0, v1)`, `v1` would be - /// of this type, and the dependency graph would have unique nodes for - /// both uses. - Indirect { - /// The instruction to which this argument belongs - inst: hir::Inst, - /// The index of this argument in the successor argument list - index: u8, - /// The index of the successor block to which this argument is bound - successor: u8, - }, - /// The argument is conditionally required by an instruction indirectly. - /// - /// This is a variation on `Indirect` which represents instructions such - /// as `cond_br` and `switch` where an argument is passed to a subset of - /// the successors for the instruction. In such cases, the argument may - /// not be used at all along the other edges, and if so, can be conditionally - /// materialized along the subset of edges which actually require it. - Conditional { - /// The instruction to which this argument belongs - inst: hir::Inst, - /// The index of this argument in the successor argument list - index: u8, - /// The successor block to which this argument is bound - successor: u8, - }, -} -impl ArgumentNode { - /// Return the instruction to which this argument belongs - #[inline] - pub fn inst(&self) -> hir::Inst { - match self { - Self::Direct { inst, .. } - | Self::Indirect { inst, .. } - | Self::Conditional { inst, .. } => *inst, - } - } - - /// Return the index of this argument in its corresponding argument list - /// - /// NOTE: Different argument types correspond to different argument lists, you - /// must make sure you are using the index returned here with the correct list. - #[inline] - pub fn index(&self) -> u8 { - match self { - Self::Direct { index, .. } - | Self::Indirect { index, .. } - | Self::Conditional { index, .. } => *index, - } - } - - /// For indirect/conditional arguments, returns the index of the successor in the - /// successor list of the instruction. - #[inline] - pub fn successor(&self) -> Option { - match self { - Self::Direct { .. } => None, - Self::Indirect { successor, .. } | Self::Conditional { successor, .. } => { - Some(*successor) - } - } - } -} -impl fmt::Debug for ArgumentNode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Direct { inst, index } => write!(f, "arg({index} of {inst})"), - Self::Indirect { - inst, - successor, - index, - } => write!(f, "block_arg({index} to {successor} of {inst})"), - Self::Conditional { - inst, - successor, - index, - } => write!(f, "conditional_block_arg({index} to {successor} of {inst})"), - } - } -} -impl Ord for ArgumentNode { - /// NOTE: This must match the ordering behavior of [NodeId] - fn cmp(&self, other: &Self) -> Ordering { - // Order by instruction, then by successor (if applicable), then by index - // - // After ordering in this way, Direct is always ordered before Indirect/Conditional, - // to account for the fact that an instruction's direct parameters always are needed - // before the successor arguments. However, Indirect/Conditional may never compare equal - // to each other after ordering based on the fields described above, because to do so - // would represent the same argument position being represented using two different, - // conflicting types. - match (self, other) { - ( - Self::Direct { - inst: x_inst, - index: xi, - }, - Self::Direct { - inst: y_inst, - index: yi, - }, - ) => x_inst.cmp(y_inst).then(xi.cmp(yi)), - ( - Self::Direct { inst: x_inst, .. }, - Self::Indirect { inst: y_inst, .. } | Self::Conditional { inst: y_inst, .. }, - ) => x_inst.cmp(y_inst).then(Ordering::Less), - ( - Self::Indirect { inst: x_inst, .. } | Self::Conditional { inst: x_inst, .. }, - Self::Direct { inst: y_inst, .. }, - ) => x_inst.cmp(y_inst).then(Ordering::Greater), - ( - Self::Indirect { - inst: x_inst, - successor: x_blk, - index: xi, - }, - Self::Indirect { - inst: y_inst, - successor: y_blk, - index: yi, - }, - ) => x_inst.cmp(y_inst).then(x_blk.cmp(y_blk)).then(xi.cmp(yi)), - ( - Self::Indirect { - inst: x_inst, - successor: x_blk, - index: xi, - }, - Self::Conditional { - inst: y_inst, - successor: y_blk, - index: yi, - }, - ) - | ( - Self::Conditional { - inst: x_inst, - successor: x_blk, - index: xi, - }, - Self::Indirect { - inst: y_inst, - successor: y_blk, - index: yi, - }, - ) => { - let result = x_inst.cmp(y_inst).then(x_blk.cmp(y_blk)).then(xi.cmp(yi)); - assert_ne!(result, Ordering::Equal, "argument node type conflict"); - result - } - ( - Self::Conditional { - inst: x_inst, - successor: x_blk, - index: xi, - }, - Self::Conditional { - inst: y_inst, - successor: y_blk, - index: yi, - }, - ) => x_inst.cmp(y_inst).then(x_blk.cmp(y_blk)).then(xi.cmp(yi)), - } - } -} -impl PartialOrd for ArgumentNode { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -#[derive(Debug, thiserror::Error)] -#[error("invalid node identifier")] -pub struct InvalidNodeIdError; - -/// Produce a bit-packed representation of [Node] which is naturally -/// sortable as if it was the expanded [Node] type. -/// -/// We currently only need 5 unique values for the node type, so we -/// use 3 bits, which gives us 7 unique values, thus we have 2 extra -/// tag values if we ever need them. This leaves us with 61 bits, of -/// which 32 is reserved for the instruction or value identifier, and -/// the remaining 29 are available for storing any type-specific data. -/// -/// We choose a layout that ensures that when compared as an integer, -/// the sort order of the corresponding [Node] would be identical. This -/// is a bit tricky, since [ArgumentNode] for example ignores the difference -/// between Indirect/Conditional argument types when sorted, so in that -/// case we use the same tag for those types, and differentiate them by -/// using one of the payload bits as a "conditional" marker. With this -/// modification, we can place the tag bits first, followed by a -/// type-specific layout which obeys the ordering rules for that type. -/// -/// The following is the key for any comments describing bit layout: -/// -/// * `t`: tag -/// * `i`: inst -/// * `v`: value -/// * `s`: successor -/// * `x`: index -/// * `c`: conditional marker -/// * `0`: unused/zero -/// -/// This is the layout used for argument nodes -/// -/// ```text,ignore -/// tttiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiissssssssxxxxxxxx000000000000c -/// |--tag (3)--|--inst (32)--|--successor (8)--|--index (8)--|--unused/zero (12)--|--conditional (1)--| -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct NodeId(u64); -impl NodeId { - const TAG_ARG_DIRECT: u64 = 1 << 60; - const TAG_ARG_INDIRECT: u64 = 2 << 60; - const TAG_INST: u64 = 3 << 60; - const TAG_RESULT: u64 = 4 << 60; - const IS_CONDITIONAL_ARG: u64 = 1; - const TAG_MASK: u64 = 0b111 << 60; - - /// Returns true if the [Node] corresponding to this identifier is of `Stack` type - #[inline] - pub fn is_stack(&self) -> bool { - self.0 & Self::TAG_MASK == 0 - } - - /// Returns true if the [Node] corresponding to this identifier is of `Result` type - #[inline] - pub fn is_result(&self) -> bool { - self.0 & Self::TAG_MASK == Self::TAG_RESULT - } - - /// Returns true if the [Node] corresponding to this identifier is of `Inst` type - #[inline] - pub fn is_instruction(&self) -> bool { - self.0 & Self::TAG_MASK == Self::TAG_INST - } - - /// Returns true if the [Node] corresponding to this identifier is of `Argument` type - #[inline] - pub fn is_argument(&self) -> bool { - matches!( - self.0 & Self::TAG_MASK, - Self::TAG_ARG_DIRECT | Self::TAG_ARG_INDIRECT - ) - } - - /// Decode this identifier into its corresponding [Node] - #[inline(always)] - pub fn expand(self) -> Node { - self.into() - } - - /// Extract the [miden_hir::Inst] associated with the corresponding [Node], or panic - /// if the node type does not have an associated instruction identifier. - pub fn unwrap_inst(self) -> hir::Inst { - let tag = self.0 & Self::TAG_MASK; - match tag { - Self::TAG_ARG_DIRECT | Self::TAG_ARG_INDIRECT => { - hir::Inst::from_u32(((self.0 >> 28) & (u32::MAX as u64)) as u32) - } - Self::TAG_INST => hir::Inst::from_u32(((self.0 >> 16) & (u32::MAX as u64)) as u32), - 0 | Self::TAG_RESULT => panic!("cannot unwrap node id as instruction: {self:?}"), - _invalid => panic!("invalid node id: {:064b}", self.0), - } - } - - /// Safely convert this identifier into a [Node]. - /// - /// This can be used in cases where the source of the [NodeId] is untrusted. - pub fn into_node(self) -> Result { - let tag = self.0 & Self::TAG_MASK; - match tag { - 0 => { - let value = (self.0 & (u32::MAX as u64)) as u32; - Ok(Node::Stack(hir::Value::from_u32(value))) - } - Self::TAG_INST => { - let pos = (self.0 & (u16::MAX as u64)) as u16; - let id = hir::Inst::from_u32(((self.0 >> 16) & (u32::MAX as u64)) as u32); - Ok(Node::Inst { id, pos }) - } - Self::TAG_ARG_DIRECT => { - let mut shifted = self.0 >> 12; - let index = (shifted & (u8::MAX as u64)) as u8; - shifted >>= 16; - let inst = (shifted & (u32::MAX as u64)) as u32; - Ok(Node::Argument(ArgumentNode::Direct { - inst: hir::Inst::from_u32(inst), - index, - })) - } - Self::TAG_ARG_INDIRECT => { - let is_conditional = self.0 & Self::IS_CONDITIONAL_ARG == Self::IS_CONDITIONAL_ARG; - let mut shifted = self.0 >> 12; - let index = (shifted & (u8::MAX as u64)) as u8; - shifted >>= 8; - let successor = (shifted & (u8::MAX as u64)) as u8; - shifted >>= 8; - let inst = hir::Inst::from_u32((shifted & (u32::MAX as u64)) as u32); - Ok(Node::Argument(if is_conditional { - ArgumentNode::Conditional { - inst, - successor, - index, - } - } else { - ArgumentNode::Indirect { - inst, - successor, - index, - } - })) - } - Self::TAG_RESULT => { - let value = hir::Value::from_u32((self.0 & (u32::MAX as u64)) as u32); - let index = ((self.0 >> 52) & (u8::MAX as u64)) as u8; - Ok(Node::Result { value, index }) - } - _ => Err(InvalidNodeIdError), - } - } -} -impl fmt::Debug for NodeId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match Node::try_from(*self) { - Ok(node) => fmt::Debug::fmt(&node, f), - Err(_) => write!(f, "InvalidNodeId({:064b})", self.0), - } - } -} -impl fmt::Display for NodeId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match Node::try_from(*self) { - Ok(node) => fmt::Display::fmt(&node, f), - Err(_) => write!(f, "InvalidNodeId({:064b})", self.0), - } - } -} -impl From for NodeId { - fn from(node: Node) -> Self { - use cranelift_entity::EntityRef; - match node { - // ttt00000000000000000000000000000vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - Node::Stack(value) => Self(value.index() as u64), - // ttt0000000000000iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxx - Node::Inst { id, pos } => { - let inst = (id.index() as u64) << 16; - let index = pos as u64; - Self(Self::TAG_INST | inst | index) - } - // tttiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiissssssssxxxxxxxx000000000000c - Node::Argument(arg) => match arg { - ArgumentNode::Direct { inst, index } => { - let inst = (inst.index() as u64) << 28; - let index = (index as u64) << 12; - Self(Self::TAG_ARG_DIRECT | inst | index) - } - ArgumentNode::Indirect { - inst, - successor, - index, - } => { - let inst = (inst.index() as u64) << 28; - let successor = (successor as u64) << 20; - let index = (index as u64) << 12; - Self(Self::TAG_ARG_INDIRECT | inst | successor | index) - } - ArgumentNode::Conditional { - inst, - successor, - index, - } => { - let inst = (inst.index() as u64) << 28; - let successor = (successor as u64) << 20; - let index = (index as u64) << 12; - Self( - Self::TAG_ARG_INDIRECT - | inst - | successor - | index - | Self::IS_CONDITIONAL_ARG, - ) - } - }, - // tttdddddddd000000000000000000000vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - Node::Result { value, index } => { - let value = value.index() as u64; - let index = (index as u64) << 52; - Self(Self::TAG_RESULT | index | value) - } - } - } -} -impl<'a> From<&'a Node> for NodeId { - #[inline] - fn from(node: &'a Node) -> Self { - (*node).into() - } -} - -/// This structure represents the relationship between dependent and -/// dependency in a [DependencyGraph]. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Dependency { - /// The node which has the dependency. - pub dependent: NodeId, - /// The node which is being depended upon. - pub dependency: NodeId, -} -impl Dependency { - /// Construct a new [Dependency]. - /// - /// In debug builds this will raise an assertion if the dependency being described - /// has nonsensical semantics. In release builds this assertion is elided. - #[inline] - pub fn new(dependent: NodeId, dependency: NodeId) -> Self { - is_valid_dependency(dependent, dependency); - Self { - dependent, - dependency, - } - } -} -impl fmt::Display for Dependency { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} => {}", self.dependent, self.dependency) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -struct Edge { - node: NodeId, - direction: Direction, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum Direction { - Dependent, - Dependency, -} - -#[derive(Debug, PartialEq, Eq)] -pub struct InvalidDependencyGraphQuery; - -/// This error type is returned by [DependencyGraph::toposort] -#[derive(Debug, thiserror::Error)] -#[error("an unexpected cycle was detected when attempting to topologically sort a treegraph")] -pub struct UnexpectedCycleError; - -/// [DependencyGraph] is a directed, acyclic graph used to represent control -/// and data dependencies in a single basic block of a function in Miden IR. -/// -/// Once constructed, we can use the graph to query information such as: -/// -/// * What is the source for each argument of an instruction -/// * Is a given instruction result used? How many times and by who? -/// * Can a given argument consume its source value, or must it be copied -/// * What node represents the last use of a value -/// * Is an instruction dead code? -/// -/// Most importantly however, a [DependencyGraph] is required in order to -/// compute a [TreeGraph] for the block in question, which is essential for -/// instruction scheduling and code generation. -#[derive(Default, Clone)] -pub struct DependencyGraph { - /// The set of nodes represented in the graph - nodes: BTreeSet, - /// A map of every node in the graph to other nodes in the graph with which it has - /// a relationship, and which dependencies describe that relationship. - edges: BTreeMap>, -} -impl DependencyGraph { - /// Create a new, empty [DependencyGraph] - #[inline] - pub fn new() -> Self { - Self::default() - } - - /// Add `node` to the dependency graph, if it is not already present - pub fn add_node(&mut self, node: Node) -> NodeId { - let id = node.id(); - if self.nodes.insert(id) { - self.edges.insert(id, Default::default()); - } - id - } - - /// Returns true if this graph contains `node` - #[inline] - pub fn contains(&self, node: &Node) -> bool { - let id = node.id(); - self.contains_id(&id) - } - - /// Returns true if this graph contains `node` - #[inline] - pub fn contains_id(&self, node: &NodeId) -> bool { - self.nodes.contains(node) - } - - /// Add a dependency from `a` to `b` - pub fn add_dependency(&mut self, a: NodeId, b: NodeId) { - assert_ne!(a, b, "cannot add a self-referential dependency"); - - let edge = Edge { - node: b, - direction: Direction::Dependent, - }; - let edges = self.edges.get_mut(&a).unwrap(); - if edges.contains(&edge) { - return; - } - edges.push(edge); - let edge = Edge { - node: a, - direction: Direction::Dependency, - }; - let edges = self.edges.get_mut(&b).unwrap(); - debug_assert!(!edges.contains(&edge)); - edges.push(edge); - } - - /// Get a [Dependency] corresponding to the edge from `from` to `to` - /// - /// This will panic if there is no edge between the two nodes given. - pub fn edge(&self, from: NodeId, to: NodeId) -> Dependency { - let edges = self.edges.get(&from).unwrap(); - let edge = Edge { - node: to, - direction: Direction::Dependent, - }; - assert!(self.nodes.contains(&from)); - assert!(self.nodes.contains(&to)); - if edges.contains(&edge) { - Dependency::new(from, to) - } else { - panic!( - "invalid edge: there is no dependency from {} to {}", - from.expand(), - to.expand(), - ); - } - } - - /// Removes `node` from the graph, along with all edges in which it appears - pub fn remove_node>(&mut self, node: N) { - let id = node.into(); - if self.nodes.remove(&id) { - let edges = self.edges.remove(&id).unwrap(); - for Edge { - node: other_node_id, - .. - } in edges.into_iter() - { - self.edges - .get_mut(&other_node_id) - .unwrap() - .retain(|e| e.node != id); - } - } - } - - /// Removes an edge from `a` to `b`. - /// - /// If `value` is provided, the use corresponding to that value is removed, rather than - /// the entire edge from `a` to `b`. However, if removing `value` makes the edge dead, or - /// `value` is not provided, then the entire edge is removed. - pub fn remove_edge(&mut self, a: NodeId, b: NodeId) { - // Get the edge id that connects a <-> b - if let Some(edges) = self.edges.get_mut(&a) { - edges.retain(|e| e.node != b || e.direction == Direction::Dependency); - } - if let Some(edges) = self.edges.get_mut(&b) { - edges.retain(|e| e.node != a || e.direction == Direction::Dependent); - } - } - - /// Returns the number of predecessors, i.e. dependents, for `node` in the graph - pub fn num_predecessors>(&self, node: N) -> usize { - let id = node.into(); - self.edges - .get(&id) - .map(|es| { - es.iter() - .filter(|e| e.direction == Direction::Dependency) - .count() - }) - .unwrap_or_default() - } - - /// Returns an iterator over the nodes in this graph - pub fn nodes(&self) -> impl Iterator + '_ { - self.nodes.iter().copied().map(Node::from) - } - - /// Returns an iterator over the nodes in this graph - pub fn node_ids(&self) -> impl Iterator + '_ { - self.nodes.iter().copied() - } - - /// Return the sole predecessor of `node`, if `node` has any predecessors. - /// - /// Returns `Err` if `node` has multiple predecessors - pub fn parent( - &self, - node: impl Into, - ) -> Result, InvalidDependencyGraphQuery> { - let mut predecessors = self.predecessors(node); - match predecessors.next() { - None => Ok(None), - Some(parent) => { - if predecessors.next().is_some() { - Err(InvalidDependencyGraphQuery) - } else { - Ok(Some(parent.dependent)) - } - } - } - } - - /// Like `parent`, but panics if `node` does not have a single parent - pub fn unwrap_parent(&self, node: impl Into) -> NodeId { - let node = node.into(); - self.parent(node) - .unwrap_or_else(|_| { - panic!("expected {node} to have a single parent, but found multiple") - }) - .unwrap_or_else(|| panic!("expected {node} to have a parent, but it has none")) - } - - /// Return the sole successor of `node`, if `node` has any successors. - /// - /// Returns `Err` if `node` has multiple successors - pub fn child( - &self, - node: impl Into, - ) -> Result, InvalidDependencyGraphQuery> { - let mut successors = self.successors(node); - match successors.next() { - None => Ok(None), - Some(child) => { - if successors.next().is_some() { - Err(InvalidDependencyGraphQuery) - } else { - Ok(Some(child.dependency)) - } - } - } - } - - /// Like `child`, but panics if `node` does not have a single child - pub fn unwrap_child(&self, node: impl Into) -> NodeId { - let node = node.into(); - self.child(node) - .unwrap_or_else(|_| { - panic!("expected {node} to have a single child, but found multiple") - }) - .unwrap_or_else(|| panic!("expected {node} to have a child, but it has none")) - } - - /// Returns an iterator over the predecessors, or dependents, of `node` in the graph - pub fn predecessors<'a, 'b: 'a>(&'b self, node: impl Into) -> Predecessors<'a> { - let id = node.into(); - Predecessors { - node: id, - iter: self.edges[&id].iter(), - } - } - - /// Like `predecessors`, but avoids decoding [Node] values, instead producing the raw [NodeId] values. - pub fn predecessor_ids(&self, node: impl Into) -> impl Iterator + '_ { - let id = node.into(); - self.edges[&id].iter().filter_map(|edge| { - if matches!(edge.direction, Direction::Dependency) { - Some(edge.node) - } else { - None - } - }) - } - - /// Returns an iterator over the successors, or dependencies, of `node` in the graph - pub fn successors<'a, 'b: 'a>(&'b self, node: impl Into) -> Successors<'a> { - let id = node.into(); - Successors { - node: id, - iter: self.edges[&id].iter(), - } - } - - /// Like `successors`, but avoids decoding [Node] values, instead producing the raw [NodeId] values. - pub fn successor_ids(&self, node: impl Into) -> impl Iterator + '_ { - let id = node.into(); - self.edges[&id].iter().filter_map(|edge| { - if matches!(edge.direction, Direction::Dependent) { - Some(edge.node) - } else { - None - } - }) - } - - /// Returns a data structure which assigns an index to each node in the graph for which `root` - /// is an ancestor, including `root` itself. The assigned index indicates the order in which nodes - /// will be emitted during code generation - the lower the index, the earlier the node is emitted. - /// Conversely, a higher index indicates that a node will be scheduled later in the program, so - /// values will be materialized from lowest index to highest. - pub fn indexed( - &self, - root: impl Into, - ) -> Result { - let root = root.into(); - - let mut output = BTreeMap::::new(); - let mut stack = vec![root]; - let mut discovered = BTreeSet::::default(); - let mut finished = BTreeSet::::default(); - - while let Some(node) = stack.last().copied() { - if discovered.insert(node) { - if node.is_instruction() { - for arg in self - .successors(node) - .filter(|succ| succ.dependency.is_argument()) - { - let arg_source_id = self.unwrap_child(arg.dependency); - if !discovered.contains(&arg_source_id) { - stack.push(arg_source_id); - } - } - for other in self - .successors(node) - .filter(|succ| !succ.dependency.is_argument()) - { - let succ_node_id = if other.dependency.is_instruction() { - other.dependency - } else { - assert!(other.dependency.is_result()); - self.unwrap_child(other.dependency) - }; - if !discovered.contains(&succ_node_id) { - stack.push(succ_node_id); - } - } - } else if node.is_result() { - let inst_node = self.unwrap_child(node); - if !discovered.contains(&inst_node) { - stack.push(inst_node); - } - } - } else { - stack.pop(); - if finished.insert(node) { - let index = output.len(); - output.insert(node, index); - } - } - } - - Ok(DependencyGraphIndices { sorted: output }) - } - - /// Get the topographically-sorted nodes of this graph for which `root` is an ancestor. - pub fn toposort(&self, root: impl Into) -> Result, UnexpectedCycleError> { - use std::collections::VecDeque; - - let root = root.into(); - let mut depgraph = self.clone(); - let mut output = Vec::::with_capacity(depgraph.nodes.len()); - - // Remove all predecessor edges to the root - if let Some(edges) = depgraph.edges.get_mut(&root) { - edges.retain(|e| e.direction == Direction::Dependent); - } - - let mut roots = VecDeque::from_iter([root]); - let mut successors = SmallVec::<[NodeId; 4]>::default(); - while let Some(nid) = roots.pop_front() { - output.push(nid); - successors.clear(); - successors.extend(depgraph.successor_ids(nid)); - for mid in successors.drain(..) { - depgraph.remove_edge(nid, mid); - if depgraph.num_predecessors(mid) == 0 { - roots.push_back(mid); - } - } - } - - let has_cycle = depgraph - .edges - .iter() - .any(|(n, es)| output.contains(n) && !es.is_empty()); - if has_cycle { - Err(UnexpectedCycleError) - } else { - Ok(output) - } - } - - /// This function is used to represent the dependency of an instruction on values - /// it uses as arguments. We do so by adding the appropriate argument node to the - /// graph, and adding edges between the instruction and the argument node, and the - /// argument node and the stack value or instruction result which it references. - pub fn add_data_dependency( - &mut self, - dependent_id: NodeId, - argument: ArgumentNode, - value: hir::Value, - pp: hir::ProgramPoint, - function: &hir::Function, - ) { - debug_assert!(dependent_id.is_instruction()); - - let dependency_id = self.add_node(Node::Argument(argument)); - match function.dfg.value_data(value) { - hir::ValueData::Inst { - inst: dep_inst, - num, - .. - } => { - let dep_inst = *dep_inst; - let block_id = function.dfg.pp_block(pp); - if function.dfg.insts[dep_inst].block == block_id { - let dep_inst_index = function - .dfg - .block_insts(block_id) - .position(|id| id == dep_inst) - .unwrap(); - let result_inst_node_id = self.add_node(Node::Inst { - id: dep_inst, - pos: dep_inst_index as u16, - }); - let result_node_id = self.add_node(Node::Result { - value, - index: *num as u8, - }); - self.add_dependency(result_node_id, result_inst_node_id); - self.add_dependency(dependency_id, result_node_id); - } else { - let operand_node_id = self.add_node(Node::Stack(value)); - self.add_dependency(dependency_id, operand_node_id); - }; - } - hir::ValueData::Param { .. } => { - let operand_node_id = self.add_node(Node::Stack(value)); - self.add_dependency(dependency_id, operand_node_id); - } - } - self.add_dependency(dependent_id, dependency_id); - } -} -impl fmt::Debug for DependencyGraph { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("DependencyGraph") - .field("nodes", &DebugNodes(self)) - .field("edges", &DebugEdges(self)) - .finish() - } -} - -/// This structure is produced by [DependencyGraph::indexed], which assigns -/// an ordinal index to every [Node] in the graph based on the order in which it -/// is visited during code generation. The lower the index, the earlier it is -/// visited. -/// -/// This is used to compare nodes in the graph with a common dependency to see which -/// one is the last dependent, which allows us to be more precise when we manipulate -/// the operand stack. -#[derive(Default)] -pub struct DependencyGraphIndices { - /// The topographically sorted nodes for the component of the - /// dependency graph for which we have constructed this set. - sorted: BTreeMap, -} -impl DependencyGraphIndices { - /// Get the index of `node` - /// - /// NOTE: This function will panic if `node` was not in the corresponding dependency graph, or is unresolved - #[inline] - pub fn get(&self, node: impl Into) -> Option { - let id = node.into(); - self.sorted.get(&id).copied() - } -} -impl fmt::Debug for DependencyGraphIndices { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_map().entries(self.sorted.iter()).finish() - } -} - -/// An iterator over each successor edge, or [Dependency], of a given node in a [DependencyGraph] -pub struct Successors<'a> { - node: NodeId, - iter: core::slice::Iter<'a, Edge>, -} -impl<'a> Iterator for Successors<'a> { - type Item = Dependency; - - fn next(&mut self) -> Option { - for Edge { node, direction } in &mut self.iter { - if matches!(direction, Direction::Dependent) { - return Some(Dependency::new(self.node, *node)); - } - } - - None - } -} -impl<'a> DoubleEndedIterator for Successors<'a> { - fn next_back(&mut self) -> Option { - while let Some(Edge { node, direction }) = self.iter.next_back() { - if matches!(direction, Direction::Dependent) { - return Some(Dependency::new(self.node, *node)); - } - } - - None - } -} -impl<'a> ExactSizeIterator for Successors<'a> { - #[inline] - fn len(&self) -> usize { - self.iter.len() - } -} - -/// An iterator over each predecessor edge, or [Dependency], of a given node in a [DependencyGraph] -pub struct Predecessors<'a> { - node: NodeId, - iter: core::slice::Iter<'a, Edge>, -} -impl<'a> Iterator for Predecessors<'a> { - type Item = Dependency; - - fn next(&mut self) -> Option { - for Edge { node, direction } in &mut self.iter { - if matches!(direction, Direction::Dependency) { - return Some(Dependency::new(*node, self.node)); - } - } - - None - } -} -impl<'a> DoubleEndedIterator for Predecessors<'a> { - fn next_back(&mut self) -> Option { - while let Some(Edge { node, direction }) = self.iter.next_back() { - if matches!(direction, Direction::Dependency) { - return Some(Dependency::new(*node, self.node)); - } - } - - None - } -} -impl<'a> ExactSizeIterator for Predecessors<'a> { - #[inline] - fn len(&self) -> usize { - self.iter.len() - } -} - -struct DebugNodes<'a>(&'a DependencyGraph); -impl<'a> fmt::Debug for DebugNodes<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.0.nodes.iter()).finish() - } -} - -struct DebugEdges<'a>(&'a DependencyGraph); -impl<'a> fmt::Debug for DebugEdges<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut edges = f.debug_list(); - for node in self.0.nodes.iter().copied() { - for edge in self.0.successors(node) { - edges.entry(&format_args!("{}", edge)); - } - } - edges.finish() - } -} - -#[cfg(debug_assertions)] -#[inline(never)] -fn is_valid_dependency(dependent: NodeId, dependency: NodeId) -> bool { - match (dependent.into(), dependency.into()) { - (Node::Argument(_), Node::Stack(_) | Node::Result { .. }) => true, - (Node::Argument(_), Node::Inst { .. } | Node::Argument(_)) => { - panic!("{dependent} -> {dependency} is invalid: arguments may only depend on results or operands"); - } - (Node::Inst { .. }, Node::Inst { .. } | Node::Result { .. } | Node::Argument(_)) => true, - (Node::Inst { .. }, _) => panic!("{dependent} -> {dependency} is invalid: instruction nodes may only depend directly on arguments"), - (Node::Result { .. }, Node::Inst { .. }) => true, - (Node::Result { .. }, _) => panic!("{dependent} -> {dependency} is invalid: result nodes may only depend directly on instructions"), - (Node::Stack(_), _) => panic!("{dependent} -> {dependency} is invalid: stack nodes may not have dependencies"), - } -} - -#[cfg(not(debug_assertions))] -#[inline(always)] -const fn is_valid_dependency(_dependent: NodeId, _dependency: NodeId) -> bool { - true -} - -/// Helper function to produce a graph for: -/// -/// ```text,ignore -/// block0(v0: i32): -/// v1 = inst0 v0 -/// v3 = inst3 -/// v2 = inst1 v1, v0 -/// inst2 v2, block1(v1), block2(v1, v0) -/// ``` -/// -/// This graph represents: -/// -/// * All node types -/// * All three argument types -/// * All types of result usage (unused, singly/multiply used) -/// * Instruction and value identifiers which are added out of order -/// with respect to program order -#[cfg(test)] -pub(crate) fn simple_dependency_graph() -> DependencyGraph { - let mut graph = DependencyGraph::new(); - let v0 = hir::Value::from_u32(0); - let v1 = hir::Value::from_u32(1); - let v2 = hir::Value::from_u32(2); - let v3 = hir::Value::from_u32(3); - let inst0 = hir::Inst::from_u32(0); - let inst1 = hir::Inst::from_u32(1); - let inst2 = hir::Inst::from_u32(2); - let inst3 = hir::Inst::from_u32(3); - - let v0_node = graph.add_node(Node::Stack(v0)); - let v1_node = graph.add_node(Node::Result { - value: v1, - index: 0, - }); - let v2_node = graph.add_node(Node::Result { - value: v2, - index: 0, - }); - let v3_node = graph.add_node(Node::Result { - value: v3, - index: 0, - }); - let inst0_node = graph.add_node(Node::Inst { id: inst0, pos: 0 }); - let inst1_node = graph.add_node(Node::Inst { id: inst1, pos: 2 }); - let inst2_node = graph.add_node(Node::Inst { id: inst2, pos: 3 }); - let inst3_node = graph.add_node(Node::Inst { id: inst3, pos: 1 }); - let inst0_arg0_node = graph.add_node(Node::Argument(ArgumentNode::Direct { - inst: inst0, - index: 0, - })); - let inst1_arg0_node = graph.add_node(Node::Argument(ArgumentNode::Direct { - inst: inst1, - index: 0, - })); - let inst1_arg1_node = graph.add_node(Node::Argument(ArgumentNode::Direct { - inst: inst1, - index: 1, - })); - let inst2_arg0_node = graph.add_node(Node::Argument(ArgumentNode::Direct { - inst: inst2, - index: 0, - })); - let inst2_block1_arg0_node = graph.add_node(Node::Argument(ArgumentNode::Indirect { - inst: inst2, - index: 0, - successor: 0, - })); - let inst2_block2_arg0_node = graph.add_node(Node::Argument(ArgumentNode::Indirect { - inst: inst2, - index: 0, - successor: 1, - })); - let inst2_block2_arg1_node = graph.add_node(Node::Argument(ArgumentNode::Conditional { - inst: inst2, - index: 1, - successor: 1, - })); - graph.add_dependency(v1_node, inst0_node); - graph.add_dependency(inst0_node, inst0_arg0_node); - graph.add_dependency(inst0_arg0_node, v0_node); - graph.add_dependency(v2_node, inst1_node); - graph.add_dependency(inst1_node, inst1_arg0_node); - graph.add_dependency(inst1_node, inst1_arg1_node); - graph.add_dependency(inst1_arg0_node, v1_node); - graph.add_dependency(inst1_arg1_node, v0_node); - graph.add_dependency(inst2_node, inst2_arg0_node); - graph.add_dependency(inst2_node, inst2_block1_arg0_node); - graph.add_dependency(inst2_node, inst2_block2_arg0_node); - graph.add_dependency(inst2_node, inst2_block2_arg1_node); - graph.add_dependency(inst2_arg0_node, v2_node); - graph.add_dependency(inst2_block1_arg0_node, v1_node); - graph.add_dependency(inst2_block2_arg0_node, v1_node); - graph.add_dependency(inst2_block2_arg1_node, v0_node); - graph.add_dependency(v3_node, inst3_node); - graph -} - -#[cfg(test)] -mod tests { - use miden_hir::{self as hir, assert_matches}; - - use super::*; - - #[test] - fn dependency_graph_construction() { - let graph = simple_dependency_graph(); - - let v0 = hir::Value::from_u32(0); - let v1 = hir::Value::from_u32(1); - let v2 = hir::Value::from_u32(2); - let v3 = hir::Value::from_u32(3); - let inst0 = hir::Inst::from_u32(0); - let inst1 = hir::Inst::from_u32(1); - let inst2 = hir::Inst::from_u32(2); - let inst3 = hir::Inst::from_u32(3); - let v0_node = Node::Stack(v0); - let v1_node = Node::Result { - value: v1, - index: 0, - }; - let v2_node = Node::Result { - value: v2, - index: 0, - }; - let v3_node = Node::Result { - value: v3, - index: 0, - }; - let inst0_node = Node::Inst { id: inst0, pos: 0 }; - let inst1_node = Node::Inst { id: inst1, pos: 2 }; - let inst2_node = Node::Inst { id: inst2, pos: 3 }; - let inst3_node = Node::Inst { id: inst3, pos: 1 }; - let inst0_arg0_node = Node::Argument(ArgumentNode::Direct { - inst: inst0, - index: 0, - }); - let inst1_arg0_node = Node::Argument(ArgumentNode::Direct { - inst: inst1, - index: 0, - }); - let inst1_arg1_node = Node::Argument(ArgumentNode::Direct { - inst: inst1, - index: 1, - }); - let inst2_arg0_node = Node::Argument(ArgumentNode::Direct { - inst: inst2, - index: 0, - }); - let inst2_block1_arg0_node = Node::Argument(ArgumentNode::Indirect { - inst: inst2, - index: 0, - successor: 0, - }); - let inst2_block2_arg0_node = Node::Argument(ArgumentNode::Indirect { - inst: inst2, - index: 0, - successor: 1, - }); - let inst2_block2_arg1_node = Node::Argument(ArgumentNode::Conditional { - inst: inst2, - index: 1, - successor: 1, - }); - - // Make sure all the nodes are in the graph - assert!(graph.contains(&v0_node)); - assert!(graph.contains(&v1_node)); - assert!(graph.contains(&v2_node)); - assert!(graph.contains(&v3_node)); - assert!(graph.contains(&inst0_node)); - assert!(graph.contains(&inst1_node)); - assert!(graph.contains(&inst2_node)); - assert!(graph.contains(&inst3_node)); - assert!(graph.contains(&inst0_arg0_node)); - assert!(graph.contains(&inst1_arg0_node)); - assert!(graph.contains(&inst1_arg1_node)); - assert!(graph.contains(&inst2_arg0_node)); - assert!(graph.contains(&inst2_block1_arg0_node)); - assert!(graph.contains(&inst2_block2_arg0_node)); - assert!(graph.contains(&inst2_block2_arg1_node)); - - // Results depend on the instructions which produce them - assert_eq!(graph.child(v1_node), Ok(Some(inst0_node.into()))); - assert_eq!(graph.child(v2_node), Ok(Some(inst1_node.into()))); - - // Instructions depend on their arguments - assert_eq!(graph.child(inst0_node), Ok(Some(inst0_arg0_node.into()))); - let mut inst1_successors = graph.successors(inst1_node).map(|s| s.dependency); - assert_eq!(inst1_successors.next(), Some(inst1_arg0_node.into())); - assert_eq!(inst1_successors.next(), Some(inst1_arg1_node.into())); - assert_eq!(inst1_successors.next(), None); - - // Arguments depend on stack values or instruction results - assert_eq!(graph.child(inst0_arg0_node), Ok(Some(v0_node.into()))); - assert_eq!(graph.child(inst1_arg0_node), Ok(Some(v1_node.into()))); - assert_eq!(graph.child(inst1_arg1_node), Ok(Some(v0_node.into()))); - assert_eq!(graph.child(inst2_arg0_node), Ok(Some(v2_node.into()))); - assert_eq!( - graph.child(inst2_block1_arg0_node), - Ok(Some(v1_node.into())) - ); - assert_eq!( - graph.child(inst2_block2_arg0_node), - Ok(Some(v1_node.into())) - ); - assert_eq!( - graph.child(inst2_block2_arg1_node), - Ok(Some(v0_node.into())) - ); - - // Arguments only have one dependent, the instruction they belong to - assert_eq!(graph.parent(inst0_arg0_node), Ok(Some(inst0_node.into()))); - assert_eq!(graph.parent(inst1_arg0_node), Ok(Some(inst1_node.into()))); - assert_eq!(graph.parent(inst1_arg1_node), Ok(Some(inst1_node.into()))); - assert_eq!(graph.parent(inst2_arg0_node), Ok(Some(inst2_node.into()))); - assert_eq!( - graph.parent(inst2_block1_arg0_node), - Ok(Some(inst2_node.into())) - ); - assert_eq!( - graph.parent(inst2_block2_arg0_node), - Ok(Some(inst2_node.into())) - ); - assert_eq!( - graph.parent(inst2_block2_arg1_node), - Ok(Some(inst2_node.into())) - ); - - // Results which are unused have no dependents - assert_eq!(graph.parent(v3_node), Ok(None)); - - // Results which are used have one or more dependents - assert_eq!(graph.parent(v2_node), Ok(Some(inst2_arg0_node.into()))); - assert_matches!(graph.parent(v1_node), Err(_)); - let mut v1_dependents = graph.predecessors(v1_node).map(|p| p.dependent); - assert_eq!(v1_dependents.next(), Some(inst1_arg0_node.into())); - assert_eq!(v1_dependents.next(), Some(inst2_block1_arg0_node.into())); - assert_eq!(v1_dependents.next(), Some(inst2_block2_arg0_node.into())); - assert_eq!(v1_dependents.next(), None); - - // Nodes with multiple dependents will raise an error if you ask for the parent - assert_matches!(graph.parent(v0_node), Err(_)); - // Stack nodes can have no dependencies - assert_eq!(graph.child(v0_node), Ok(None)); - } - - /// We're expecting the graph to correspond to the following expression graph - /// - /// ```text,ignore - /// inst2 - /// |- inst2_arg0 -> v2 -> inst1--------- - /// | | - /// | _____________ - /// | | | - /// | inst1_arg0 inst1_arg1 - /// | | | - /// | v | - /// |- inst2_block1_arg0 ------> v1 -> inst0 | - /// | ^ | | - /// | | v | - /// | | inst0_arg0 | - /// |- inst2_block2_arg0 -------- | | - /// | v | - /// |- inst2_block2_arg1 -------------> v0 <--- - /// ``` - /// - /// Which should correspond to the following index assignment: - /// - /// 0. v0 - /// 1. inst0 - /// 2. result(v1) - /// 3. inst1 - /// 4. result(v2) - /// 5. inst2 - /// - /// For reference, this is the IR we have a graph of: - /// - /// ```text,ignore - /// block0(v0: i32): - /// v1 = inst0 v0 - /// v3 = inst3 - /// v2 = inst1 v1, v0 - /// inst2 v2, block1(v1), block2(v1, v0) - /// ``` - #[test] - fn dependency_graph_indexed() { - let graph = simple_dependency_graph(); - - let v0 = hir::Value::from_u32(0); - let v1 = hir::Value::from_u32(1); - let v2 = hir::Value::from_u32(2); - let inst0 = hir::Inst::from_u32(0); - let inst1 = hir::Inst::from_u32(1); - let inst2 = hir::Inst::from_u32(2); - let inst3 = hir::Inst::from_u32(3); - let v0_node = Node::Stack(v0); - let v1_node = Node::Result { - value: v1, - index: 0, - }; - let v2_node = Node::Result { - value: v2, - index: 0, - }; - let inst0_node = Node::Inst { id: inst0, pos: 0 }; - let inst1_node = Node::Inst { id: inst1, pos: 2 }; - let inst2_node = Node::Inst { id: inst2, pos: 3 }; - let inst3_node = Node::Inst { id: inst3, pos: 1 }; - - let indices = graph.indexed(inst2_node).unwrap(); - - assert_eq!(indices.get(inst3_node), None); - assert_eq!(indices.get(inst2_node), Some(5)); - assert_eq!(indices.get(v2_node), Some(4)); - assert_eq!(indices.get(inst1_node), Some(3)); - assert_eq!(indices.get(v1_node), Some(2)); - assert_eq!(indices.get(inst0_node), Some(1)); - assert_eq!(indices.get(v0_node), Some(0)); - } -} diff --git a/hir-analysis/src/dominance.rs b/hir-analysis/src/dominance.rs deleted file mode 100644 index ca8720e6b..000000000 --- a/hir-analysis/src/dominance.rs +++ /dev/null @@ -1,949 +0,0 @@ -use std::{ - cmp::{self, Ordering}, - mem, -}; - -use cranelift_entity::packed_option::PackedOption; -use cranelift_entity::SecondaryMap; - -use rustc_hash::FxHashSet; - -use miden_hir::pass::{Analysis, AnalysisManager, AnalysisResult, PreservedAnalyses}; -use miden_hir::{Block, BranchInfo, DataFlowGraph, Function, Inst, ProgramPoint}; -use midenc_session::Session; - -use super::{BlockPredecessor, ControlFlowGraph}; - -/// RPO numbers are assigned as multiples of STRIDE to leave room -/// for modifications to the dominator tree. -const STRIDE: u32 = 4; - -/// A special RPO number used during `compute_postorder`. -const SEEN: u32 = 1; - -/// A node in the dominator tree. Each block has one of these. -#[derive(Clone, Default)] -struct Node { - /// Number of this node in a reverse post-order traversal of the control-flow graph, starting from 1. - /// - /// This number is monotonic in the reverse post-order but not contiguous, as we leave holes for - /// localized modifications of the dominator tree after it is initially computed. - /// - /// Unreachable nodes get number 0, all others are > 0. - rpo_number: u32, - /// The immediate dominator of this block, represented as the instruction at the end of the dominating - /// block which transfers control to this block. - /// - /// This is `None` for unreachable blocks, as well as the entry block, which has no dominators. - idom: PackedOption, -} - -/// DFT stack state marker for computing the cfg post-order. -enum Visit { - First, - Last, -} - -#[derive(Default)] -pub struct DominatorTree { - nodes: SecondaryMap, - /// Post-order of all reachable blocks in the control flow graph - postorder: Vec, - /// Scratch buffer used by `compute_postorder` - stack: Vec<(Visit, Block)>, - valid: bool, -} -impl Analysis for DominatorTree { - type Entity = Function; - - fn analyze( - function: &Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> AnalysisResult { - let cfg = analyses.get_or_compute(function, session)?; - Ok(DominatorTree::with_function(function, &cfg)) - } - - fn is_invalidated(&self, preserved: &PreservedAnalyses) -> bool { - !preserved.is_preserved::() - } -} -impl DominatorTree { - /// Allocate a new blank dominator tree. Use `compute` to compute the dominator tree for a - /// function. - pub fn new() -> Self { - Self::default() - } - - /// Allocate and compute a dominator tree. - pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self { - let block_capacity = func.dfg.num_blocks(); - let mut domtree = Self { - nodes: SecondaryMap::with_capacity(block_capacity), - postorder: Vec::with_capacity(block_capacity), - stack: Vec::new(), - valid: false, - }; - domtree.compute(func, cfg); - domtree - } - - /// Reset and compute a CFG post-order and dominator tree. - pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph) { - debug_assert!(cfg.is_valid()); - self.compute_postorder(func); - self.compute_domtree(func, cfg); - self.valid = true; - } - - /// Clear the data structures used to represent the dominator tree. This will leave the tree in - /// a state where `is_valid()` returns false. - pub fn clear(&mut self) { - self.nodes.clear(); - self.postorder.clear(); - debug_assert!(self.stack.is_empty()); - self.valid = false; - } - - /// Check if the dominator tree is in a valid state. - /// - /// Note that this doesn't perform any kind of validity checks. It simply checks if the - /// `compute()` method has been called since the last `clear()`. It does not check that the - /// dominator tree is consistent with the CFG. - pub fn is_valid(&self) -> bool { - self.valid - } - - /// Is `block` reachable from the entry block? - pub fn is_reachable(&self, block: Block) -> bool { - self.nodes[block].rpo_number != 0 - } - - /// Get the blocks in cfg post-order used to compute the dominator tree. - /// - /// NOTE: This order is not updated automatically when the control-flow graph is modified, - /// it is computed from scratch and cached by `compute`. - pub fn cfg_postorder(&self) -> &[Block] { - debug_assert!(self.is_valid()); - &self.postorder - } - - /// Returns the immediate dominator of `block`. - /// - /// The immediate dominator of a basic block is the instruction which transfers control to that - /// block (and implicitly, its enclosing block). This instruction does not have to be the terminator - /// of its block, though it typically is. - /// - /// An instruction "dominates" `block` if all control flow paths from the function entry to `block` - /// must go through that instruction. - /// - /// The "immediate dominator" is the dominator that is closest to `block`. All other dominators - /// also dominate the immediate dominator. - /// - /// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block - /// which has no dominators. - pub fn idom(&self, block: Block) -> Option { - self.nodes[block].idom.into() - } - - /// Compare two blocks relative to the reverse post-order. - pub fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering { - self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number) - } - - /// Compare two program points relative to a reverse post-order traversal of the control-flow - /// graph. - /// - /// Return `Ordering::Less` if `a` comes before `b` in the RPO. - /// - /// If `a` and `b` belong to the same block, compare their relative position in the block. - pub fn rpo_cmp(&self, a: A, b: B, dfg: &DataFlowGraph) -> Ordering - where - A: Into, - B: Into, - { - let a = a.into(); - let b = b.into(); - self.rpo_cmp_block(dfg.pp_block(a), dfg.pp_block(b)) - .then_with(|| dfg.pp_cmp(a, b)) - } - - /// Returns `true` if `a` dominates `b`. - /// - /// This means that every control-flow path from the function entry to `b` must go through `a`. - /// - /// Dominance is ill defined for unreachable blocks. This function can always determine - /// dominance for instructions in the same block, but otherwise returns `false` if either block - /// is unreachable. - /// - /// An instruction is considered to dominate itself. - pub fn dominates(&self, a: A, b: B, dfg: &DataFlowGraph) -> bool - where - A: Into, - B: Into, - { - let a = a.into(); - let b = b.into(); - match a { - ProgramPoint::Block(block_a) => { - a == b || self.last_dominator(block_a, b, dfg).is_some() - } - ProgramPoint::Inst(inst_a) => { - let block_a = dfg.inst_block(inst_a).expect("Instruction not in layout."); - match self.last_dominator(block_a, b, dfg) { - Some(last) => dfg.pp_cmp(inst_a, last) != Ordering::Greater, - None => false, - } - } - } - } - - /// Find the last instruction in `a` that dominates `b`. - /// If no instructions in `a` dominate `b`, return `None`. - pub fn last_dominator(&self, a: Block, b: B, dfg: &DataFlowGraph) -> Option - where - B: Into, - { - let (mut block_b, mut inst_b) = match b.into() { - ProgramPoint::Block(block) => (block, None), - ProgramPoint::Inst(inst) => ( - dfg.inst_block(inst).expect("Instruction not in layout."), - Some(inst), - ), - }; - let rpo_a = self.nodes[a].rpo_number; - - // Run a finger up the dominator tree from b until we see a. - // Do nothing if b is unreachable. - while rpo_a < self.nodes[block_b].rpo_number { - let idom = match self.idom(block_b) { - Some(idom) => idom, - None => return None, // a is unreachable, so we climbed past the entry - }; - block_b = dfg.inst_block(idom).expect("Dominator got removed."); - inst_b = Some(idom); - } - if a == block_b { - inst_b - } else { - None - } - } - - /// Compute the common dominator of two basic blocks. - /// - /// Both basic blocks are assumed to be reachable. - pub fn common_dominator( - &self, - mut a: BlockPredecessor, - mut b: BlockPredecessor, - dfg: &DataFlowGraph, - ) -> BlockPredecessor { - loop { - match self.rpo_cmp_block(a.block, b.block) { - Ordering::Less => { - // `a` comes before `b` in the RPO. Move `b` up. - let idom = self.nodes[b.block].idom.expect("Unreachable basic block?"); - b = BlockPredecessor::new( - dfg.inst_block(idom).expect("Dangling idom instruction"), - idom, - ); - } - Ordering::Greater => { - // `b` comes before `a` in the RPO. Move `a` up. - let idom = self.nodes[a.block].idom.expect("Unreachable basic block?"); - a = BlockPredecessor::new( - dfg.inst_block(idom).expect("Dangling idom instruction"), - idom, - ); - } - Ordering::Equal => break, - } - } - - debug_assert_eq!( - a.block, b.block, - "Unreachable block passed to common_dominator?" - ); - - // We're in the same block. The common dominator is the earlier instruction. - if dfg.pp_cmp(a.inst, b.inst) == Ordering::Less { - a - } else { - b - } - } - - /// Reset all internal data structures and compute a post-order of the control flow graph. - /// - /// This leaves `rpo_number == 1` for all reachable blocks, 0 for unreachable ones. - fn compute_postorder(&mut self, func: &Function) { - self.clear(); - self.nodes.resize(func.dfg.num_blocks()); - - // This algorithm is a depth first traversal (DFT) of the control flow graph, computing a - // post-order of the blocks that are reachable form the entry block. A DFT post-order is not - // unique. The specific order we get is controlled by the order each node's children are - // visited. - // - // We view the CFG as a graph where each `BlockCall` value of a terminating branch - // instruction is an edge. A consequence of this is that we visit successor nodes in the - // reverse order specified by the branch instruction that terminates the basic block. - // (Reversed because we are using a stack to control traversal, and push the successors in - // the order the branch instruction specifies -- there's no good reason for this particular - // order.) - // - // During this algorithm only, use `rpo_number` to hold the following state: - // - // 0: block has not yet had its first visit - // SEEN: block has been visited at least once, implying that all of its successors are on - // the stack - self.stack.push((Visit::First, func.dfg.entry_block())); - - while let Some((visit, block)) = self.stack.pop() { - match visit { - Visit::First => { - if self.nodes[block].rpo_number == 0 { - // This is the first time we pop the block, so we need to scan its - // successors and then revisit it. - self.nodes[block].rpo_number = SEEN; - self.stack.push((Visit::Last, block)); - if let Some(inst) = func.dfg.last_inst(block) { - // Heuristic: chase the children in reverse. This puts the first - // successor block first in the postorder, all other things being - // equal, which tends to prioritize loop backedges over out-edges, - // putting the edge-block closer to the loop body and minimizing - // live-ranges in linear instruction space. This heuristic doesn't have - // any effect on the computation of dominators, and is purely for other - // 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::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)); - } - } - } - } - } - } - } - - Visit::Last => { - // We've finished all this node's successors. - self.postorder.push(block); - } - } - } - } - - /// Build a dominator tree from a control flow graph using Keith D. Cooper's - /// "Simple, Fast Dominator Algorithm." - fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) { - // During this algorithm, `rpo_number` has the following values: - // - // 0: block is not reachable. - // 1: block is reachable, but has not yet been visited during the first pass. This is set by - // `compute_postorder`. - // 2+: block is reachable and has an assigned RPO number. - - // We'll be iterating over a reverse post-order of the CFG, skipping the entry block. - let (entry_block, postorder) = match self.postorder.as_slice().split_last() { - Some((&eb, rest)) => (eb, rest), - None => return, - }; - - // Do a first pass where we assign RPO numbers to all reachable nodes. - self.nodes[entry_block].rpo_number = 2 * STRIDE; - for (rpo_idx, &block) in postorder.iter().rev().enumerate() { - // Update the current node and give it an RPO number. - // The entry block got 2, the rest start at 3 by multiples of STRIDE to leave - // room for future dominator tree modifications. - // - // Since `compute_idom` will only look at nodes with an assigned RPO number, the - // function will never see an uninitialized predecessor. - // - // Due to the nature of the post-order traversal, every node we visit will have at - // least one predecessor that has previously been visited during this RPO. - self.nodes[block] = Node { - idom: self.compute_idom(block, cfg, &func.dfg).into(), - rpo_number: (rpo_idx as u32 + 3) * STRIDE, - } - } - - // Now that we have RPO numbers for everything and initial immediate dominator estimates, - // iterate until convergence. - // - // If the function is free of irreducible control flow, this will exit after one iteration. - let mut changed = true; - while changed { - changed = false; - for &block in postorder.iter().rev() { - let idom = self.compute_idom(block, cfg, &func.dfg).into(); - if self.nodes[block].idom != idom { - self.nodes[block].idom = idom; - changed = true; - } - } - } - } - - // Compute the immediate dominator for `block` using the current `idom` states for the reachable - // nodes. - fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph, dfg: &DataFlowGraph) -> Inst { - // Get an iterator with just the reachable, already visited predecessors to `block`. - // Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't - // been visited yet, 0 for unreachable blocks. - let mut reachable_preds = cfg - .pred_iter(block) - .filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1); - - // The RPO must visit at least one predecessor before this node. - let mut idom = reachable_preds - .next() - .expect("block node must have one reachable predecessor"); - - for pred in reachable_preds { - idom = self.common_dominator(idom, pred, dfg); - } - - idom.inst - } -} - -/// Auxiliary structure for `DominatorTree` which provides: -/// -/// - Traversal of the dominator tree in pre-order -/// - Ordering of blocks in dominator tree pre-order -/// - Constant-time dominance checks per-block -#[derive(Default)] -pub struct DominatorTreePreorder { - nodes: SecondaryMap, - stack: Vec, -} - -#[derive(Default, Clone)] -struct PreorderNode { - /// First child node in the dominator tree - child: Option, - /// Next sibling node in the dominator tree, ordered - /// according to the control-flow graph reverse post-order. - sibling: Option, - /// Sequence number for this node in a pre-order traversal of the dominator tree - /// - /// Unreachable blocks are 0, entry block is 1 - pre_number: u32, - /// Maximum `pre_number` for the sub-tree of the dominator tree that is rooted at this node. - /// - /// This is always greater than or equal to `pre_number` - pre_max: u32, -} -impl DominatorTreePreorder { - pub fn new() -> Self { - Self::default() - } - - pub fn with_function(domtree: &DominatorTree, function: &Function) -> Self { - let mut this = Self::new(); - this.compute(domtree, function); - this - } - - pub fn compute(&mut self, domtree: &DominatorTree, function: &Function) { - self.nodes.clear(); - debug_assert_eq!(self.stack.len(), 0); - - // Step 1: Populate the child and sibling links. - // - // By following the CFG post-order and pushing to the front of the lists, we make sure that - // sibling lists are ordered according to the CFG reverse post-order. - for &block in domtree.cfg_postorder() { - if let Some(idom_inst) = domtree.idom(block) { - let idom = function.dfg.inst_block(idom_inst).unwrap(); - let sib = mem::replace(&mut self.nodes[idom].child, Some(block)); - self.nodes[block].sibling = sib; - } else { - // The only block without an immediate dominator is the entry. - self.stack.push(block); - } - } - - // Step 2. Assign pre-order numbers from a DFS of the dominator tree. - debug_assert!(self.stack.len() <= 1); - let mut n = 0; - while let Some(block) = self.stack.pop() { - n += 1; - let node = &mut self.nodes[block]; - node.pre_number = n; - node.pre_max = n; - if let Some(n) = node.sibling { - self.stack.push(n); - } - if let Some(n) = node.child { - self.stack.push(n); - } - } - - // Step 3. Propagate the `pre_max` numbers up the tree. - // The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all - // its dominator tree children. - for &block in domtree.cfg_postorder() { - if let Some(idom_inst) = domtree.idom(block) { - let idom = function.dfg.inst_block(idom_inst).unwrap(); - let pre_max = cmp::max(self.nodes[block].pre_max, self.nodes[idom].pre_max); - self.nodes[idom].pre_max = pre_max; - } - } - } - - /// Get an iterator over the immediate children of `block` in the dominator tree. - /// - /// These are the blocks whose immediate dominator is an instruction in `block`, ordered according - /// to the CFG reverse post-order. - pub fn children(&self, block: Block) -> ChildIter { - ChildIter { - dtpo: self, - next: self.nodes[block].child, - } - } - - /// Fast, constant time dominance check with block granularity. - /// - /// This computes the same result as `domtree.dominates(a, b)`, but in guaranteed fast constant - /// time. This is less general than the `DominatorTree` method because it only works with block - /// program points. - /// - /// A block is considered to dominate itself. - pub fn dominates(&self, a: Block, b: Block) -> bool { - let na = &self.nodes[a]; - let nb = &self.nodes[b]; - na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max - } - - /// Compare two blocks according to the dominator pre-order. - pub fn pre_cmp_block(&self, a: Block, b: Block) -> Ordering { - self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number) - } - - /// Compare two program points according to the dominator tree pre-order. - /// - /// This ordering of program points have the property that given a program point, pp, all the - /// program points dominated by pp follow immediately and contiguously after pp in the order. - pub fn pre_cmp(&self, a: A, b: B, function: &Function) -> Ordering - where - A: Into, - B: Into, - { - let a = a.into(); - let b = b.into(); - self.pre_cmp_block(function.dfg.pp_block(a), function.dfg.pp_block(b)) - .then_with(|| function.dfg.pp_cmp(a, b)) - } -} - -/// An iterator that enumerates the direct children of a block in the dominator tree. -pub struct ChildIter<'a> { - dtpo: &'a DominatorTreePreorder, - next: Option, -} - -impl<'a> Iterator for ChildIter<'a> { - type Item = Block; - - fn next(&mut self) -> Option { - let n = self.next; - if let Some(block) = n { - self.next = self.dtpo.nodes[block].sibling; - } - n - } -} - -/// Calculates the dominance frontier for every block in a given `DominatorTree` -/// -/// A dominance frontier of a block `B` is the set of blocks `N` where control flow -/// join points exist, where multiple value definitions together as one. -/// -/// More formally, the dominance frontier is every block `Ni` in `N` where the following -/// properties hold: -/// -/// * `B` dominates an immediate predecessor of `Ni` -/// * `B` does not strictly dominate `Ni`; strict dominance is when `B` dominates -/// `Ni`, but `B != Ni` -/// -/// Consider the following example: -/// -/// -/// ```text,ignore -/// block0(v0): -/// v1 = ... -/// cond_br v0, block1, block2 -/// -/// block1(): -/// br block3(v1) -/// -/// block2(): -/// v2 = ... -/// br block3(v2) -/// -/// block3(v3): -/// ... -/// ``` -/// -/// Here, `block0` strictly dominates all other blocks; but neither `block1` or `block2` -/// dominate `block3`. This tells us that `block3` must be in the dominance frontier of `block1` -/// and `block2`, because: -/// -/// * By definition, every block dominates itself, but does not strictly dominate itself -/// * Both `block1` and `block2` are immediate predecessors of `block3` -/// * Thus, both `block1` and `block2` technically dominate a predecessor of `block3` -/// * Neither `block1` nor `block2` strictly dominate `block3` -/// -/// It is also obvious that `block3` must be in the dominance frontier of `block1` and `block2`, -/// because we can observe that `block3` is a join point for control that flows through `block1` and -/// `block2` - the value of `v3` depends on which path is taken to reach `block3`. -/// -/// You might wonder if `block3` is in the dominance frontier of `block0`, and the answer is no. -/// That's because `block0` strictly dominates `block3`, i.e. all control flow must pass through it -/// to reach `block3`. The reason why strict dominance matters becomes more clear when you consider -/// that any value defined in `block0` will have the same definition same regardless of which path is -/// taken to reach `block3`. -/// -/// ## Purpose -/// -/// The dominance frontier is used to place new phi nodes (which in our IR are represented by block arguments) -/// after introducing register spills/reloads. Reloads would naturally introduce multiple definitions for -/// a given value, which would break the SSA property of the IR, so to preserve it, reloads introduce new -/// definitions, and all uses of the original definition dominated by the reload are updated. -/// -/// However, that alone is insufficient, since there may be uses of the original definition which are _not_ -/// dominated by the reload due to branching control flow. To address this, we must introduce new block -/// arguments to every block in the dominance frontier of the block in which reloads occur, and where -/// the reloaded value is live. All uses of either the original definition dominated by that phi node are -/// rewritten to use the definition produced by the phi. -/// -/// The actual algorithm works bottom-up, rather than top-down, but the relationship to the dominance frontier -/// is the same in both cases. -#[derive(Default)] -pub struct DominanceFrontier { - /// The dominance frontier for each block, as a set of blocks - dfs: SecondaryMap>, -} -impl DominanceFrontier { - pub fn compute(domtree: &DominatorTree, cfg: &ControlFlowGraph, function: &Function) -> Self { - let mut dfs = SecondaryMap::>::default(); - - for id in domtree.cfg_postorder() { - let id = *id; - if cfg.num_predecessors(id) < 2 { - continue; - } - let idom = domtree.idom(id).unwrap(); - for BlockPredecessor { block: p, inst: i } in cfg.pred_iter(id) { - let mut p = p; - let mut i = i; - while i != idom { - dfs[p].insert(id); - let Some(idom_p) = domtree.idom(p) else { - break; - }; - i = idom_p; - p = function.dfg.inst_block(idom_p).unwrap(); - } - } - } - - Self { dfs } - } - - /// Get an iterator over the dominance frontier of `block` - pub fn iter(&self, block: &Block) -> impl Iterator + '_ { - DominanceFrontierIter { - df: self.dfs.get(*block).map(|set| set.iter().copied()), - } - } - - /// Get the set of blocks in the dominance frontier of `block`, - /// or `None` if `block` has an empty dominance frontier. - #[inline] - pub fn get(&self, block: &Block) -> Option<&FxHashSet> { - self.dfs.get(*block) - } -} - -struct DominanceFrontierIter { - df: Option, -} -impl<'a, I> Iterator for DominanceFrontierIter -where - I: Iterator + 'a, -{ - type Item = Block; - - fn next(&mut self) -> Option { - if let Some(i) = self.df.as_mut() { - i.next() - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::ControlFlowGraph; - use miden_hir::{ - AbiParam, Function, FunctionBuilder, Immediate, InstBuilder, Signature, SourceSpan, Type, - }; - - #[test] - fn domtree_empty() { - let id = "test::empty".parse().unwrap(); - let function = Function::new(id, Signature::new([], [])); - let entry = function.dfg.entry_block(); - - let cfg = ControlFlowGraph::with_function(&function); - assert!(cfg.is_valid()); - let domtree = DominatorTree::with_function(&function, &cfg); - - assert_eq!(1, domtree.nodes.keys().count()); - assert_eq!(domtree.cfg_postorder(), &[entry]); - - let mut dtpo = DominatorTreePreorder::new(); - dtpo.compute(&domtree, &function); - } - - #[test] - fn domtree_unreachable_node() { - let id = "test::unreachable_node".parse().unwrap(); - let mut function = Function::new(id, Signature::new([AbiParam::new(Type::I32)], [])); - let block0 = function.dfg.entry_block(); - let block1 = function.dfg.create_block(); - let block2 = function.dfg.create_block(); - let trap_block = function.dfg.create_block(); - let v2 = { - let mut builder = FunctionBuilder::new(&mut function); - let v0 = { - let args = builder.block_params(block0); - args[0] - }; - - builder.switch_to_block(block0); - let cond = builder - .ins() - .neq_imm(v0, Immediate::I32(0), SourceSpan::UNKNOWN); - builder - .ins() - .cond_br(cond, block2, &[], trap_block, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(trap_block); - builder.ins().unreachable(SourceSpan::UNKNOWN); - - builder.switch_to_block(block1); - let v1 = builder.ins().i32(1, SourceSpan::UNKNOWN); - let v2 = builder.ins().add_checked(v0, v1, SourceSpan::UNKNOWN); - builder.ins().br(block0, &[v2], SourceSpan::UNKNOWN); - - builder.switch_to_block(block2); - builder.ins().ret(Some(v0), SourceSpan::UNKNOWN); - v2 - }; - - let cfg = ControlFlowGraph::with_function(&function); - let domtree = DominatorTree::with_function(&function, &cfg); - - // Fall-through-first, prune-at-source DFT: - // - // block0 { - // brif block2 { - // trap - // block2 { - // return - // } block2 - // } block0 - assert_eq!(domtree.cfg_postorder(), &[block2, trap_block, block0]); - - let v2_inst = function.dfg.value_data(v2).unwrap_inst(); - assert!(!domtree.dominates(v2_inst, block0, &function.dfg)); - assert!(!domtree.dominates(block0, v2_inst, &function.dfg)); - - let mut dtpo = DominatorTreePreorder::new(); - dtpo.compute(&domtree, &function); - assert!(dtpo.dominates(block0, block0)); - assert!(!dtpo.dominates(block0, block1)); - assert!(dtpo.dominates(block0, block2)); - assert!(!dtpo.dominates(block1, block0)); - assert!(dtpo.dominates(block1, block1)); - assert!(!dtpo.dominates(block1, block2)); - assert!(!dtpo.dominates(block2, block0)); - assert!(!dtpo.dominates(block2, block1)); - assert!(dtpo.dominates(block2, block2)); - } - - #[test] - fn domtree_non_zero_entry_block() { - let id = "test::non_zero_entry".parse().unwrap(); - let mut function = Function::new(id, Signature::new([], [])); - let block0 = function.dfg.entry_block(); - let block1 = function.dfg.create_block(); - let block2 = function.dfg.create_block(); - let block3 = function.dfg.create_block(); - let cond = function - .dfg - .append_block_param(block3, Type::I1, SourceSpan::UNKNOWN); - function.dfg.entry = block3; - function.signature.params.push(AbiParam::new(Type::I1)); - let (br_block3_block1, br_block1_block0_block2) = { - let mut builder = FunctionBuilder::new(&mut function); - - builder.switch_to_block(block3); - let br_block3_block1 = builder.ins().br(block1, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block1); - let br_block1_block0_block2 = - builder - .ins() - .cond_br(cond, block0, &[], block2, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block2); - builder.ins().br(block0, &[], SourceSpan::UNKNOWN); - - (br_block3_block1, br_block1_block0_block2) - }; - - let cfg = ControlFlowGraph::with_function(&function); - let domtree = DominatorTree::with_function(&function, &cfg); - - // Fall-through-first, prune-at-source DFT: - // - // block3 { - // block3:jump block1 { - // block1 { - // block1:brif block0 { - // block1:jump block2 { - // block2 { - // block2:jump block0 (seen) - // } block2 - // } block1:jump block2 - // block0 { - // } block0 - // } block1:brif block0 - // } block1 - // } block3:jump block1 - // } block3 - - assert_eq!(domtree.cfg_postorder(), &[block0, block2, block1, block3]); - - assert_eq!(function.dfg.entry_block(), block3); - assert_eq!(domtree.idom(block3), None); - assert_eq!(domtree.idom(block1).unwrap(), br_block3_block1); - assert_eq!(domtree.idom(block2).unwrap(), br_block1_block0_block2); - assert_eq!(domtree.idom(block0).unwrap(), br_block1_block0_block2); - - assert!(domtree.dominates( - br_block1_block0_block2, - br_block1_block0_block2, - &function.dfg - )); - assert!(!domtree.dominates(br_block1_block0_block2, br_block3_block1, &function.dfg)); - assert!(domtree.dominates(br_block3_block1, br_block1_block0_block2, &function.dfg)); - - assert_eq!( - domtree.rpo_cmp(block3, block3, &function.dfg), - Ordering::Equal - ); - assert_eq!( - domtree.rpo_cmp(block3, block1, &function.dfg), - Ordering::Less - ); - assert_eq!( - domtree.rpo_cmp(block3, br_block3_block1, &function.dfg), - Ordering::Less - ); - assert_eq!( - domtree.rpo_cmp(br_block3_block1, br_block1_block0_block2, &function.dfg), - Ordering::Less - ); - } - - #[test] - fn domtree_backwards_layout() { - let id = "test::backwards_layout".parse().unwrap(); - let mut function = Function::new(id, Signature::new([], [])); - let block0 = function.dfg.entry_block(); - let block1 = function.dfg.create_block(); - let block2 = function.dfg.create_block(); - let (jmp02, trap, jmp21) = { - let mut builder = FunctionBuilder::new(&mut function); - - builder.switch_to_block(block0); - let jmp02 = builder.ins().br(block2, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block1); - let trap = builder.ins().unreachable(SourceSpan::UNKNOWN); - - builder.switch_to_block(block2); - let jmp21 = builder.ins().br(block1, &[], SourceSpan::UNKNOWN); - - (jmp02, trap, jmp21) - }; - - let cfg = ControlFlowGraph::with_function(&function); - let domtree = DominatorTree::with_function(&function, &cfg); - - assert_eq!(function.dfg.entry_block(), block0); - assert_eq!(domtree.idom(block0), None); - assert_eq!(domtree.idom(block1), Some(jmp21)); - assert_eq!(domtree.idom(block2), Some(jmp02)); - - assert!(domtree.dominates(block0, block0, &function.dfg)); - assert!(domtree.dominates(block0, jmp02, &function.dfg)); - assert!(domtree.dominates(block0, block1, &function.dfg)); - assert!(domtree.dominates(block0, trap, &function.dfg)); - assert!(domtree.dominates(block0, block2, &function.dfg)); - assert!(domtree.dominates(block0, jmp21, &function.dfg)); - - assert!(!domtree.dominates(jmp02, block0, &function.dfg)); - assert!(domtree.dominates(jmp02, jmp02, &function.dfg)); - assert!(domtree.dominates(jmp02, block1, &function.dfg)); - assert!(domtree.dominates(jmp02, trap, &function.dfg)); - assert!(domtree.dominates(jmp02, block2, &function.dfg)); - assert!(domtree.dominates(jmp02, jmp21, &function.dfg)); - - assert!(!domtree.dominates(block1, block0, &function.dfg)); - assert!(!domtree.dominates(block1, jmp02, &function.dfg)); - assert!(domtree.dominates(block1, block1, &function.dfg)); - assert!(domtree.dominates(block1, trap, &function.dfg)); - assert!(!domtree.dominates(block1, block2, &function.dfg)); - assert!(!domtree.dominates(block1, jmp21, &function.dfg)); - - assert!(!domtree.dominates(trap, block0, &function.dfg)); - assert!(!domtree.dominates(trap, jmp02, &function.dfg)); - assert!(!domtree.dominates(trap, block1, &function.dfg)); - assert!(domtree.dominates(trap, trap, &function.dfg)); - assert!(!domtree.dominates(trap, block2, &function.dfg)); - assert!(!domtree.dominates(trap, jmp21, &function.dfg)); - - assert!(!domtree.dominates(block2, block0, &function.dfg)); - assert!(!domtree.dominates(block2, jmp02, &function.dfg)); - assert!(domtree.dominates(block2, block1, &function.dfg)); - assert!(domtree.dominates(block2, trap, &function.dfg)); - assert!(domtree.dominates(block2, block2, &function.dfg)); - assert!(domtree.dominates(block2, jmp21, &function.dfg)); - - assert!(!domtree.dominates(jmp21, block0, &function.dfg)); - assert!(!domtree.dominates(jmp21, jmp02, &function.dfg)); - assert!(domtree.dominates(jmp21, block1, &function.dfg)); - assert!(domtree.dominates(jmp21, trap, &function.dfg)); - assert!(!domtree.dominates(jmp21, block2, &function.dfg)); - assert!(domtree.dominates(jmp21, jmp21, &function.dfg)); - } -} diff --git a/hir-analysis/src/lib.rs b/hir-analysis/src/lib.rs deleted file mode 100644 index 06d427e3f..000000000 --- a/hir-analysis/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod control_flow; -mod data; -pub mod dependency_graph; -mod dominance; -mod liveness; -mod loops; -mod treegraph; -mod validation; - -pub use self::control_flow::{BlockPredecessor, ControlFlowGraph}; -pub use self::data::{GlobalVariableAnalysis, GlobalVariableLayout}; -pub use self::dependency_graph::DependencyGraph; -pub use self::dominance::{DominanceFrontier, DominatorTree, DominatorTreePreorder}; -pub use self::liveness::LivenessAnalysis; -pub use self::loops::{Loop, LoopAnalysis, LoopLevel}; -pub use self::treegraph::{OrderedTreeGraph, TreeGraph}; -pub use self::validation::{ModuleValidationAnalysis, Rule}; diff --git a/hir-analysis/src/liveness.rs b/hir-analysis/src/liveness.rs deleted file mode 100644 index 315fd80a3..000000000 --- a/hir-analysis/src/liveness.rs +++ /dev/null @@ -1,759 +0,0 @@ -use std::{ - cmp, - collections::{BTreeMap, VecDeque}, -}; - -use miden_hir::pass::{Analysis, AnalysisManager, AnalysisResult, PreservedAnalyses}; -use miden_hir::{self as hir, Block as BlockId, Inst as InstId, Value as ValueId, *}; -use midenc_session::Session; -use rustc_hash::FxHashMap; - -use super::{ControlFlowGraph, DominatorTree, LoopAnalysis}; - -/// This data structure is the result of computing liveness information over the -/// control flow graph of an HIR function. It uses a somewhat novel approach to -/// liveness that is ideally suited to optimal register allocation over programs -/// in SSA form, as described in _Register Spilling and Live-Range Splitting for -/// SSA-Form Programs_, by Matthias Braun and Sebastian Hack. -/// -/// In short, rather than simply tracking what values are live at a particular -/// program point, we instead track the global next-use distance for all values -/// which are live at a given program point. Just like the more typical liveness -/// analysis, the next-use analysis computes next-use distances for a block by -/// taking the next-use distances at the exit of the block, and working backwards -/// to compute the next-use distances at the entry of the block. When computing -/// the next-use distances at the exit of a block with multiple successors, the -/// join function takes the minimum next-use distances of all variables live across -/// some edge. -/// -/// By default, variables introduced by an instruction (i.e. their definition) are -/// assigned the next-use distance at the next instruction (or the block exit). If -/// no use of the variable has been observed, it is assigned a next-use distance of -/// infinity (here represented as `u32::MAX`). Each time we step backwards up through -/// the block, we increment the distance of all values observed so far by 1. If we -/// encounter a use of a variable at the current instruction, its next-use distance -/// is set to 0. -/// -/// When we calculate the next-use distances for the exit of a block, the set is -/// initialized by taking the join of the next-use distances at the entry of all -/// successors of that block, or the empty set if there are no successors. However, -/// if the current block is inside a loop, and any successor is outside that loop, -/// then all next-use distances obtained from that successor are incremented by a -/// large constant (1000 in our case). -/// -/// The algorithm follows the standard dataflow analysis approach of working until -/// a fixpoint is reached. We start by visiting the control flow graph in reverse -/// postorder, and revisit any blocks whose results change since the last time we -/// saw that block. -/// -/// The resulting data structure is ideal for register allocation, but the real benefit -/// is that it allows us to be smart about how we spill values, since it can tell us -/// how "hot" a value is, allowing us to prioritize such values over those which may -/// not be used for awhile. -#[derive(Debug, Default)] -pub struct LivenessAnalysis { - // Liveness/global next-uses at a given program point - live_in: FxHashMap, - // Liveness/global next-uses after a given program point - live_out: FxHashMap, - // Maximum register pressure for each block - per_block_register_pressure: FxHashMap, -} -impl Analysis for LivenessAnalysis { - type Entity = Function; - - fn analyze( - function: &Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> AnalysisResult { - let cfg = analyses.get_or_compute(function, session)?; - let domtree = analyses.get_or_compute(function, session)?; - let loops = analyses.get_or_compute(function, session)?; - Ok(LivenessAnalysis::compute(function, &cfg, &domtree, &loops)) - } - - fn is_invalidated(&self, preserved: &PreservedAnalyses) -> bool { - !preserved.is_preserved::() - || !preserved.is_preserved::() - || !preserved.is_preserved::() - } -} -impl LivenessAnalysis { - /// Computes liveness for the given function, using the provided analyses - pub fn compute( - function: &Function, - cfg: &ControlFlowGraph, - domtree: &DominatorTree, - loops: &LoopAnalysis, - ) -> Self { - let mut liveness = Self::default(); - liveness.recompute(function, cfg, domtree, loops); - liveness - } - - /// Recomputes liveness for the given function, using the provided analyses - pub fn recompute( - &mut self, - function: &Function, - cfg: &ControlFlowGraph, - domtree: &DominatorTree, - loops: &LoopAnalysis, - ) { - self.clear(); - compute_liveness(self, function, cfg, domtree, loops) - } - - /// Clear all computed liveness information, without releasing the memory we allocated - pub fn clear(&mut self) { - self.live_in.clear(); - self.live_out.clear(); - self.per_block_register_pressure.clear(); - } - - /// Returns true if `value` is live at the given program point. - /// - /// To be live "at" means that the value is live entering that point of - /// the program, but does not imply that it is live after that point. - pub fn is_live_at(&self, value: &ValueId, pp: ProgramPoint) -> bool { - self.live_in[&pp].contains(value) - } - - /// Returns true if `value` is live after the given program point. - /// - /// To be live "after" means that the value is live exiting that point of - /// the program, but does not imply that it is live before that point. - pub fn is_live_after(&self, value: &ValueId, pp: ProgramPoint) -> bool { - self.live_out[&pp].contains(value) - } - - /// Returns the next-use distance of `value` at the given program point. - pub fn next_use(&self, value: &ValueId, pp: ProgramPoint) -> u32 { - self.live_in[&pp].distance(value) - } - - /// Returns the next-use distance of `value` after the given program point. - pub fn next_use_after(&self, value: &ValueId, pp: ProgramPoint) -> u32 { - self.live_out[&pp].distance(value) - } - - /// Returns the global next-use distances at the given program point - pub fn next_uses(&self, pp: ProgramPoint) -> &NextUseSet { - &self.live_in[&pp] - } - - /// Returns the global next-use distances at the given program point - pub fn next_uses_after(&self, pp: ProgramPoint) -> &NextUseSet { - &self.live_out[&pp] - } - - /// Returns an iterator over values which are live at the given program point - pub fn live_at(&self, pp: ProgramPoint) -> impl Iterator + '_ { - self.live_in[&pp] - .iter() - .filter_map(|(v, dist)| if dist < &u32::MAX { Some(*v) } else { None }) - } - - /// Returns an iterator over values which are live after the given program point - pub fn live_after(&self, pp: ProgramPoint) -> impl Iterator + '_ { - self.live_out[&pp] - .iter() - .filter_map(|(v, dist)| if dist < &u32::MAX { Some(*v) } else { None }) - } - - /// Returns the maximum register pressure in the given block - pub fn max_register_pressure(&self, block: &BlockId) -> usize { - self.per_block_register_pressure[block] - } - - /// Returns the chromatic number of the interference graph implicit in the - /// liveness data represented here, i.e. number of colors required to color - /// the graph such that no two adjacent nodes share the same color, i.e. the - /// minimum number of registers needed to perform register allocation over - /// the function without spills. - /// - /// In a more practical sense, this returns the maximum number of live values - /// at any one point in the analyzed function. - /// - /// # Explanation - /// - /// Because the interference graphs of SSA-form programs are chordal graphs, - /// and chordal graphs are "perfect", this makes certain properties of the interference - /// graph easy to derive. In particular, the chromatic number of a perfect graph is - /// equal to the size of its largest clique (group of nodes which form a complete graph, - /// i.e. have edges to each other). - pub fn chromatic_number(&self) -> usize { - let mut max = 0; - for (_pp, next_used) in self.live_in.iter() { - max = cmp::max( - next_used.iter().filter(|(_, d)| *d < &u32::MAX).count(), - max, - ); - } - max - } -} - -/// This data structure is used to maintain a mapping from variables to -/// their next-use distance at a specific program point. -/// -/// If a value is not in the set, we have not observed its definition, or -/// any uses at the associated program point. -#[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct NextUseSet(BTreeMap); -impl FromIterator<(ValueId, u32)> for NextUseSet { - fn from_iter(iter: T) -> Self - where - T: IntoIterator, - { - let mut set = Self::default(); - for (k, v) in iter.into_iter() { - set.insert(k, v); - } - set - } -} -impl NextUseSet { - /// Inserts `value` in this set with the given `distance`. - /// - /// A distance of `u32::MAX` signifies infinite distance, which is - /// equivalent to saying that `value` is not live. - /// - /// If `value` is already in this set, the distance is updated to be the - /// lesser of the two distances, e.g. if the previous distance was `u32::MAX`, - /// and `distance` was `1`, the entry is updated to have a distance of `1` after - /// this function returns. - pub fn insert(&mut self, value: ValueId, distance: u32) { - use std::collections::btree_map::Entry; - match self.0.entry(value) { - Entry::Vacant(entry) => { - entry.insert(distance); - } - Entry::Occupied(mut entry) => { - let prev_distance = entry.get_mut(); - *prev_distance = std::cmp::min(*prev_distance, distance); - } - } - } - - /// Returns `true` if `value` is live in this set - #[inline] - pub fn is_live(&self, value: &ValueId) -> bool { - self.distance(value) < u32::MAX - } - - /// Returns the distance to the next use of `value` as an integer. - /// - /// If `value` is not live, or the distance is unknown, returns `u32::MAX` - pub fn distance(&self, value: &ValueId) -> u32 { - self.0.get(value).copied().unwrap_or(u32::MAX) - } - - /// Returns `true` if `value` is in this set - #[inline] - pub fn contains(&self, value: &ValueId) -> bool { - self.0.contains_key(value) - } - - /// Gets the distance associated with the given `value`, if known - #[inline] - pub fn get(&self, value: &ValueId) -> Option<&u32> { - self.0.get(value) - } - - /// Gets a mutable reference to the distance associated with the given `value`, if known - #[inline] - pub fn get_mut(&mut self, value: &ValueId) -> Option<&mut u32> { - self.0.get_mut(value) - } - - pub fn entry( - &mut self, - value: ValueId, - ) -> std::collections::btree_map::Entry<'_, ValueId, u32> { - self.0.entry(value) - } - - /// Removes the entry for `value` from this set - pub fn remove(&mut self, value: &ValueId) -> Option { - self.0.remove(value) - } - - /// Returns a new set containing the union of `self` and `other`. - /// - /// The resulting set will preserve the minimum distances from both sets. - pub fn union(&self, other: &Self) -> Self { - let mut result = self.clone(); - for (k, v) in other.iter() { - result.insert(*k, *v); - } - result - } - - /// Returns a new set containing the intersection of `self` and `other`. - /// - /// The resulting set will preserve the minimum distances from both sets. - pub fn intersection(&self, other: &Self) -> Self { - let mut result = Self::default(); - for (k, v1) in self.iter() { - match other.get(k) { - None => continue, - Some(v2) => { - result.0.insert(*k, core::cmp::min(*v1, *v2)); - } - } - } - result - } - - /// Returns a new set containing the symmetric difference of `self` and `other`, - /// i.e. the values that are in `self` or `other` but not in both. - pub fn symmetric_difference(&self, other: &Self) -> Self { - let mut result = Self::default(); - for (k, v) in self.iter() { - if !other.0.contains_key(k) { - result.0.insert(*k, *v); - } - } - for (k, v) in other.iter() { - if !self.0.contains_key(k) { - result.0.insert(*k, *v); - } - } - result - } - - pub fn iter(&self) -> impl Iterator { - self.0.iter() - } - - pub fn iter_mut(&mut self) -> impl Iterator { - self.0.iter_mut() - } - - pub fn keys(&self) -> impl Iterator + '_ { - self.0.keys().copied() - } - - /// Remove the value in this set which is closest compared to the others - /// - /// If this set is empty, returns `None`. - /// - /// If more than one value have the same distance, this returns the value with - /// the lowest id first. - #[inline] - pub fn pop_first(&mut self) -> Option<(ValueId, u32)> { - self.0.pop_first() - } - - /// Remove the value in this set which is furthest away compared to the others - /// - /// If this set is empty, returns `None`. - /// - /// If more than one value have the same distance, this returns the value with - /// the highest id first. - #[inline] - pub fn pop_last(&mut self) -> Option<(ValueId, u32)> { - self.0.pop_last() - } -} -impl<'a, 'b> std::ops::BitOr<&'b NextUseSet> for &'a NextUseSet { - type Output = NextUseSet; - - #[inline] - fn bitor(self, rhs: &'b NextUseSet) -> Self::Output { - self.union(rhs) - } -} -impl<'a, 'b> std::ops::BitAnd<&'b NextUseSet> for &'a NextUseSet { - type Output = NextUseSet; - - #[inline] - fn bitand(self, rhs: &'b NextUseSet) -> Self::Output { - self.intersection(rhs) - } -} -impl<'a, 'b> std::ops::BitXor<&'b NextUseSet> for &'a NextUseSet { - type Output = NextUseSet; - - #[inline] - fn bitxor(self, rhs: &'b NextUseSet) -> Self::Output { - self.symmetric_difference(rhs) - } -} - -/// This function computes global next-use distances/liveness until a fixpoint is reached. -/// -/// The resulting data structure associates two [NextUseSet]s with every block and instruction in `function`. -/// One set provides next-use distances _at_ a given program point, the other _after_ a given program point. -/// Intuitively, these are basically what they sound like. If a value is used "at" a given instruction, it's -/// next-use distance will be 0, and if it is never used after that, it won't be present in the "after" set. -/// However, if it used again later, it's distance in the "after" set will be the distance from the instruction -/// following the current one (or the block exit if the current one is a terminator). -fn compute_liveness( - liveness: &mut LivenessAnalysis, - function: &Function, - cfg: &ControlFlowGraph, - domtree: &DominatorTree, - loops: &LoopAnalysis, -) { - use std::collections::hash_map::Entry; - - // The distance penalty applied to an edge which exits a loop - const LOOP_EXIT_DISTANCE: u32 = 100_000; - - let mut worklist = VecDeque::new(); - for block in domtree.cfg_postorder().iter().copied() { - worklist.push_back(block); - } - - // Compute liveness and global next-uses for each block, bottom-up - // - // For each block, liveness is determined by the next-use distance for - // each variable, where a distance of `u32::MAX` is our representation of - // infinity, and infinity is the distance assigned a value which is dead. - // All values implicitly start with an infinite distance, and the distance - // to next use is calculated by taking the length of control flow edges (0 - // for regular edges, LOOP_EXIT_DISTANCE for edges which leave a loop) and - // adding that to the next-use distances of values which are live over the edge. - // - // The next-use distance for a value within a block, is the distance between - // that use and it's definition in terms of the number of instructions in the block - // which precede it. As such, we visit instructions in a block bottom-up, where at - // each program point, we record the following next-use information: - // - // * Values defined by the current instruction are given the next-use distances - // observed thus far at that instruction, e.g. if no use of a value is observed, - // that distance is `u32::MAX`. For all preceding instructions, the value is - // removed from the next-use set. - // if the value is used by the next instruction the distance is `1`, and so on - // * Values used by the current instruction are given a next-use distance of `0` - // * All other values known at the current program point have their next-use distances - // incremented by 1 - while let Some(block_id) = worklist.pop_front() { - let block = &function.dfg.blocks[block_id]; - let block_loop = loops.innermost_loop(block_id); - let mut max_register_pressure = 0; - let mut inst_cursor = block.insts.back(); - while let Some(inst_data) = inst_cursor.get() { - let inst = inst_data.key; - let pp = ProgramPoint::Inst(inst); - let branch_info = inst_data.analyze_branch(&function.dfg.value_lists); - - // Compute the initial next-use set of this program point - // - // * For terminators which are branches, next-use distances are given by the next-use - // distances live over the control-flow graph edges. Multiple successors are handled by - // joining the next-use sets of all edges. In both cases, block arguments of successors - // are removed from the set, unless the successor dominates the instruction. - // * For all other terminators (i.e. `ret`), the next-use set is empty. - // * For regular instructions, the next-use set is initialized using the next-use set of - // the succeeding instruction, with all distances incremented by 1, but with all values - // defined by the successor removed from the set. - // - let (inst_next_uses, inst_next_uses_after) = match branch_info { - // This is either a non-branching terminator, or a regular instruction - BranchInfo::NotABranch => { - let succ_cursor = inst_cursor.peek_next(); - if let Some(succ) = succ_cursor.get() { - // Start with the next-use set of the next instruction - let mut inst_next_uses = liveness - .live_in - .entry(ProgramPoint::Inst(succ.key)) - .or_default() - .clone(); - - // Increment the next-use distance for all inherited next uses - for (_value, dist) in inst_next_uses.iter_mut() { - *dist = dist.saturating_add(1); - } - - // The next uses up to this point forms the next-use set after `inst` - let mut inst_next_uses_after = inst_next_uses.clone(); - - // Add uses by this instruction to the live-in set, with a distance of 0 - for arg in inst_data - .arguments(&function.dfg.value_lists) - .iter() - .copied() - { - inst_next_uses.insert(arg, 0); - } - - // Remove all instruction results from the live-in set, and ensure they have - // a default distance set in the live-out set - for result in function.dfg.inst_results(inst) { - inst_next_uses.remove(result); - inst_next_uses_after.entry(*result).or_insert(u32::MAX); - } - - (inst_next_uses, inst_next_uses_after) - } else { - // This must be a non-branching terminator, i.e. the `ret` instruction - // Thus, the next-use set after `inst` is empty initially - assert!(inst_data.opcode().is_terminator()); - - // Add uses by this instruction to the live-in set, with a distance of 0 - let mut inst_next_uses = NextUseSet::default(); - for arg in inst_data - .arguments(&function.dfg.value_lists) - .iter() - .copied() - { - inst_next_uses.insert(arg, 0); - } - - // Add results to the live-out set - - (inst_next_uses, NextUseSet::default()) - } - } - // 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, extra_uses) => { - let mut inst_next_uses = liveness - .live_in - .entry(ProgramPoint::Block(succ)) - .or_default() - .clone(); - - // Increment the next-use distance for all inherited next uses - for (_value, dist) in inst_next_uses.iter_mut() { - *dist = dist.saturating_add(1); - } - - // The next uses up to this point forms the initial next-use set after `inst` - let mut inst_next_uses_after = inst_next_uses.clone(); - - // Add uses by this instruction to the live-in set, with a distance of 0 - for arg in inst_data - .arguments(&function.dfg.value_lists) - .iter() - .copied() - .chain(extra_uses.iter().copied()) - { - inst_next_uses.insert(arg, 0); - } - - // Remove the successor block arguments from live-in/live-out, as those values - // cannot be live before they are defined, and even if the destination block - // dominates the current block (via loop), those values must be dead at this - // instruction as we're providing new definitions for them. - for value in function.dfg.block_args(succ) { - // Only remove them from live-in if they are not actually used though, - // since, for example, a loopback branch to a loop header could pass - // a value that was defined via that header's block parameters. - if !extra_uses.contains(value) { - inst_next_uses.remove(value); - } - inst_next_uses_after.remove(value); - } - - // If this block is in a loop, make sure we add the loop exit distance for edges leaving the loop - if let Some(block_loop) = block_loop { - let succ_loop = loops.innermost_loop(succ); - let is_loop_exit = succ_loop - .map(|l| block_loop != l && loops.is_child_loop(block_loop, l)) - .unwrap_or(true); - if is_loop_exit { - for (_, dist) in inst_next_uses.iter_mut() { - *dist = dist.saturating_add(LOOP_EXIT_DISTANCE); - } - for (_, dist) in inst_next_uses_after.iter_mut() { - *dist = dist.saturating_add(LOOP_EXIT_DISTANCE); - } - } - } - (inst_next_uses, inst_next_uses_after) - } - // Same as above - // - // NOTE: We additionally assert here that all critical edges in the control flow 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) => { - let mut inst_next_uses = NextUseSet::default(); - let mut inst_next_uses_after = NextUseSet::default(); - for JumpTable { - destination, - args: extra_uses, - } in jts.iter() - { - let destination = *destination; - // If the successor block has multiple predecessors, this is a critical edge, as by - // definition this instruction means the current block has multiple successors - assert_eq!( - cfg.num_predecessors(destination), - 1, - "expected all critical edges of {} to have been split!", - destination, - ); - let mut jt_next_uses = liveness - .live_in - .entry(ProgramPoint::Block(destination)) - .or_default() - .clone(); - - // Increment the next-use distance for all inherited next uses - for (_value, dist) in jt_next_uses.iter_mut() { - *dist = dist.saturating_add(1); - } - - // The next uses up to this point forms the initial next-use set after `inst` - let mut jt_next_uses_after = jt_next_uses.clone(); - - // Add uses by this instruction to the live-in set, with a distance of 0 - for arg in inst_data - .arguments(&function.dfg.value_lists) - .iter() - .copied() - .chain(extra_uses.iter().copied()) - { - jt_next_uses.insert(arg, 0); - } - - // Remove the successor block arguments from live-in/live-out, as those values - // cannot be live before they are defined, and even if the destination block - // dominates the current block (via loop), those values must be dead at this - // instruction as we're providing new definitions for them. - for value in function.dfg.block_args(destination) { - // Only remove them from live-in if they are not actually used though, - // since, for example, a loopback branch to a loop header could pass - // a value that was defined via that header's block parameters. - if !extra_uses.contains(value) { - jt_next_uses.remove(value); - } - jt_next_uses_after.remove(value); - } - - // If this block is in a loop, make sure we add the loop exit distance for edges leaving the loop - if let Some(block_loop) = block_loop { - let succ_loop = loops.innermost_loop(destination); - let is_loop_exit = succ_loop - .map(|l| block_loop != l && loops.is_child_loop(block_loop, l)) - .unwrap_or(true); - if is_loop_exit { - for (_, dist) in jt_next_uses.iter_mut() { - *dist = dist.saturating_add(LOOP_EXIT_DISTANCE); - } - for (_, dist) in jt_next_uses_after.iter_mut() { - *dist = dist.saturating_add(LOOP_EXIT_DISTANCE); - } - } - } - inst_next_uses = inst_next_uses.union(&jt_next_uses); - inst_next_uses_after = inst_next_uses_after.union(&jt_next_uses_after); - } - (inst_next_uses, inst_next_uses_after) - } - }; - - // The maximum register pressure for this block is the greatest number of - // live-in values at any point within the block. - max_register_pressure = cmp::max( - max_register_pressure, - inst_next_uses - .iter() - .filter(|(_, d)| *d < &u32::MAX) - .count(), - ); - - // Record the next-use distances for this program point - liveness.live_in.insert(pp, inst_next_uses); - liveness.live_out.insert(pp, inst_next_uses_after); - - // Move to the instruction preceding this one - inst_cursor.move_prev(); - } - - // Handle the block header - let pp = ProgramPoint::Block(block_id); - // The block header derives it's next-use distances from the live-in set of it's first instruction - let first_inst = block.insts.front().get().unwrap().key; - let mut block_next_uses = liveness.live_in[&ProgramPoint::Inst(first_inst)].clone(); - // For each block argument, make sure a default next-use distance (u32::MAX) is set - // if a distance is not found in the live-in set of the first instruction - for arg in block - .params - .as_slice(&function.dfg.value_lists) - .iter() - .copied() - { - block_next_uses.entry(arg).or_insert(u32::MAX); - } - // For blocks, the "after" set corresponds to the next-use set "after" the block - // terminator. This makes it easy to query liveness at entry and exit to a block. - let last_inst = block.insts.back().get().unwrap().key; - let block_next_uses_after = liveness.live_out[&ProgramPoint::Inst(last_inst)].clone(); - // Re-enqueue this block until analysis reaches a stable state, i.e. the next-use - // distances remain unchanged. - match liveness.live_in.entry(pp) { - Entry::Vacant(entry) => { - entry.insert(block_next_uses); - liveness.live_out.insert(pp, block_next_uses_after); - // We haven't visited this block before, so re-enqueue it as by - // definition the next-use set has changed. - worklist.push_back(block_id); - } - Entry::Occupied(mut entry) => { - let prev = entry.get(); - // We've seen this block before, but if nothing has changed since - // our last visit, then we have reached a fixpoint. Otherwise, - // we must re-enqueue it again. - if prev != &block_next_uses { - worklist.push_back(block_id); - } - entry.insert(block_next_uses); - liveness.live_out.insert(pp, block_next_uses_after); - } - } - liveness - .per_block_register_pressure - .insert(block_id, max_register_pressure); - } -} - -impl hir::Decorator for &LivenessAnalysis { - type Display<'a> = DisplayLiveness<'a> where Self: 'a; - - fn decorate_block<'a, 'l: 'a>(&'l self, block: BlockId) -> Self::Display<'a> { - DisplayLiveness { - pp: ProgramPoint::Block(block), - lr: self, - } - } - - fn decorate_inst<'a, 'l: 'a>(&'l self, inst: InstId) -> Self::Display<'a> { - DisplayLiveness { - pp: ProgramPoint::Inst(inst), - lr: self, - } - } -} - -struct NextUse<'a> { - value: &'a ValueId, - distance: &'a u32, -} -impl<'a> core::fmt::Display for NextUse<'a> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{}:{}", self.value, self.distance) - } -} - -#[doc(hidden)] -pub struct DisplayLiveness<'a> { - pp: ProgramPoint, - lr: &'a LivenessAnalysis, -} -impl<'a> core::fmt::Display for DisplayLiveness<'a> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - let live = self - .lr - .next_uses(self.pp) - .iter() - .map(|(value, distance)| NextUse { value, distance }); - write!(f, " # next_used=[{}]", DisplayValues::new(live),) - } -} diff --git a/hir-analysis/src/loops.rs b/hir-analysis/src/loops.rs deleted file mode 100644 index 2f521c7b1..000000000 --- a/hir-analysis/src/loops.rs +++ /dev/null @@ -1,585 +0,0 @@ -use cranelift_entity::packed_option::PackedOption; -use cranelift_entity::{entity_impl, PrimaryMap, SecondaryMap}; - -use miden_hir::pass::{Analysis, AnalysisManager, AnalysisResult, PreservedAnalyses}; -use miden_hir::{Block, DataFlowGraph, Function}; -use midenc_session::Session; - -use super::{BlockPredecessor, ControlFlowGraph, DominatorTree}; - -/// Represents a loop in the loop tree of a function -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Loop(u32); -entity_impl!(Loop, "loop"); - -/// A level in a loop nest. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct LoopLevel(u8); -impl LoopLevel { - const INVALID: u8 = u8::MAX; - - /// Get the root level (no loop). - pub const fn root() -> Self { - Self(0) - } - - /// Get the loop level. - pub const fn level(self) -> usize { - self.0 as usize - } - - /// Invalid loop level. - pub const fn invalid() -> Self { - Self(Self::INVALID) - } - - /// One loop level deeper. - pub fn inc(self) -> Self { - if self.0 == (Self::INVALID - 1) { - self - } else { - Self(self.0 + 1) - } - } - - /// A clamped loop level from a larger-width (usize) depth. - pub fn clamped(level: usize) -> Self { - Self( - u8::try_from(std::cmp::min(level, (Self::INVALID as usize) - 1)) - .expect("invalid clamped loop level"), - ) - } -} -impl Default for LoopLevel { - fn default() -> Self { - LoopLevel::invalid() - } -} - -struct LoopData { - header: Block, - parent: PackedOption, - level: LoopLevel, -} -impl LoopData { - /// Creates a `LoopData` object with the loop header and its eventual parent in the loop tree. - pub fn new(header: Block, parent: Option) -> Self { - Self { - header, - parent: parent.into(), - level: LoopLevel::invalid(), - } - } -} - -/// Loop tree information for a single function. -/// -/// Loops are referenced by the `Loop` type, and for each loop you can -/// access its header block, its eventual parent in the loop tree, and -/// all the blocks belonging to the loop. -pub struct LoopAnalysis { - loops: PrimaryMap, - block_loop_map: SecondaryMap>, - valid: bool, -} -impl Analysis for LoopAnalysis { - type Entity = Function; - - fn analyze( - function: &Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> AnalysisResult { - let cfg = analyses.get_or_compute(function, session)?; - let domtree = analyses.get_or_compute(function, session)?; - Ok(LoopAnalysis::with_function(function, &cfg, &domtree)) - } - - fn is_invalidated(&self, preserved: &PreservedAnalyses) -> bool { - !preserved.is_preserved::() || !preserved.is_preserved::() - } -} -impl Default for LoopAnalysis { - fn default() -> Self { - Self { - valid: false, - loops: PrimaryMap::new(), - block_loop_map: SecondaryMap::new(), - } - } -} -impl LoopAnalysis { - pub fn with_function( - function: &Function, - cfg: &ControlFlowGraph, - domtree: &DominatorTree, - ) -> Self { - let mut this = Self::default(); - this.compute(function, cfg, domtree); - this - } - - /// Detects the loops in a function. Needs the control flow graph and the dominator tree. - pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) { - self.loops.clear(); - self.block_loop_map.clear(); - self.block_loop_map.resize(func.dfg.num_blocks()); - self.find_loop_headers(cfg, domtree, &func.dfg); - self.discover_loop_blocks(cfg, domtree, &func.dfg); - self.assign_loop_levels(); - self.valid = true; - } - - /// Check if the loop analysis is in a valid state. - /// - /// Note that this doesn't perform any kind of validity checks. It simply checks if the - /// `compute()` method has been called since the last `clear()`. It does not check that the - /// loop analysis is consistent with the CFG. - pub fn is_valid(&self) -> bool { - self.valid - } - - /// Clear all the data structures contained in the loop analysis. This will leave the - /// analysis in a similar state to a context returned by `new()` except that allocated - /// memory be retained. - pub fn clear(&mut self) { - self.loops.clear(); - self.block_loop_map.clear(); - self.valid = false; - } - - /// Returns all the loops contained in a function. - pub fn loops(&self) -> cranelift_entity::Keys { - self.loops.keys() - } - - /// Returns the header block of a particular loop. - /// - /// The characteristic property of a loop header block is that it dominates some of its - /// predecessors. - pub fn loop_header(&self, lp: Loop) -> Block { - self.loops[lp].header - } - - /// Return the eventual parent of a loop in the loop tree. - pub fn loop_parent(&self, lp: Loop) -> Option { - self.loops[lp].parent.expand() - } - - /// Return the innermost loop for a given block. - pub fn innermost_loop(&self, block: Block) -> Option { - self.block_loop_map[block].expand() - } - - /// Determine if a Block is a loop header. If so, return the loop. - pub fn is_loop_header(&self, block: Block) -> Option { - self.innermost_loop(block) - .filter(|&lp| self.loop_header(lp) == block) - } - - /// Determine if a Block belongs to a loop by running a finger along the loop tree. - /// - /// Returns `true` if `block` is in loop `lp`. - pub fn is_in_loop(&self, block: Block, lp: Loop) -> bool { - let block_loop = self.block_loop_map[block]; - match block_loop.expand() { - None => false, - Some(block_loop) => self.is_child_loop(block_loop, lp), - } - } - - /// Determines if a loop is contained in another loop. - /// - /// `is_child_loop(child,parent)` returns `true` if and only if `child` is a child loop of - /// `parent` (or `child == parent`). - pub fn is_child_loop(&self, child: Loop, parent: Loop) -> bool { - let mut finger = Some(child); - while let Some(finger_loop) = finger { - if finger_loop == parent { - return true; - } - finger = self.loop_parent(finger_loop); - } - false - } - - /// Returns the loop-nest level of a given block. - pub fn loop_level(&self, block: Block) -> LoopLevel { - self.innermost_loop(block) - .map_or(LoopLevel(0), |lp| self.loops[lp].level) - } - - /// Returns the loop level of the given level - pub fn level(&self, lp: Loop) -> LoopLevel { - self.loops[lp].level - } - - // Traverses the CFG in reverse postorder and create a loop object for every block having a - // back edge. - fn find_loop_headers( - &mut self, - cfg: &ControlFlowGraph, - domtree: &DominatorTree, - dfg: &DataFlowGraph, - ) { - // We traverse the CFG in reverse postorder - for &block in domtree.cfg_postorder().iter().rev() { - for BlockPredecessor { - inst: pred_inst, .. - } in cfg.pred_iter(block) - { - // If the block dominates one of its predecessors it is a back edge - if domtree.dominates(block, pred_inst, dfg) { - // This block is a loop header, so we create its associated loop - let lp = self.loops.push(LoopData::new(block, None)); - self.block_loop_map[block] = lp.into(); - break; - // We break because we only need one back edge to identify a loop header. - } - } - } - } - - // Intended to be called after `find_loop_headers`. For each detected loop header, - // discovers all the block belonging to the loop and its inner loops. After a call to this - // function, the loop tree is fully constructed. - fn discover_loop_blocks( - &mut self, - cfg: &ControlFlowGraph, - domtree: &DominatorTree, - dfg: &DataFlowGraph, - ) { - let mut stack: Vec = Vec::new(); - // We handle each loop header in reverse order, corresponding to a pseudo postorder - // traversal of the graph. - for lp in self.loops().rev() { - for BlockPredecessor { - block: pred, - inst: pred_inst, - } in cfg.pred_iter(self.loops[lp].header) - { - // We follow the back edges - if domtree.dominates(self.loops[lp].header, pred_inst, dfg) { - stack.push(pred); - } - } - while let Some(node) = stack.pop() { - let continue_dfs: Option; - match self.block_loop_map[node].expand() { - None => { - // The node hasn't been visited yet, we tag it as part of the loop - self.block_loop_map[node] = PackedOption::from(lp); - continue_dfs = Some(node); - } - Some(node_loop) => { - // We copy the node_loop into a mutable reference passed along the while - let mut node_loop = node_loop; - // The node is part of a loop, which can be lp or an inner loop - let mut node_loop_parent_option = self.loops[node_loop].parent; - while let Some(node_loop_parent) = node_loop_parent_option.expand() { - if node_loop_parent == lp { - // We have encountered lp so we stop (already visited) - break; - } else { - // - node_loop = node_loop_parent; - // We lookup the parent loop - node_loop_parent_option = self.loops[node_loop].parent; - } - } - // Now node_loop_parent is either: - // - None and node_loop is an new inner loop of lp - // - Some(...) and the initial node_loop was a known inner loop of lp - match node_loop_parent_option.expand() { - Some(_) => continue_dfs = None, - None => { - if node_loop != lp { - self.loops[node_loop].parent = lp.into(); - continue_dfs = Some(self.loops[node_loop].header) - } else { - // If lp is a one-block loop then we make sure we stop - continue_dfs = None - } - } - } - } - } - // Now we have handled the popped node and need to continue the DFS by adding the - // predecessors of that node - if let Some(continue_dfs) = continue_dfs { - for BlockPredecessor { block: pred, .. } in cfg.pred_iter(continue_dfs) { - stack.push(pred) - } - } - } - } - } - - fn assign_loop_levels(&mut self) { - use smallvec::{smallvec, SmallVec}; - - let mut stack: SmallVec<[Loop; 8]> = smallvec![]; - for lp in self.loops.keys() { - if self.loops[lp].level == LoopLevel::invalid() { - stack.push(lp); - while let Some(&lp) = stack.last() { - if let Some(parent) = self.loops[lp].parent.into() { - if self.loops[parent].level != LoopLevel::invalid() { - self.loops[lp].level = self.loops[parent].level.inc(); - stack.pop(); - } else { - stack.push(parent); - } - } else { - self.loops[lp].level = LoopLevel::root().inc(); - stack.pop(); - } - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ControlFlowGraph, DominatorTree}; - use miden_hir::{ - AbiParam, Function, FunctionBuilder, InstBuilder, Signature, SourceSpan, Type, - }; - - #[test] - fn nested_loops_variant1_detection() { - let id = "test::nested_loops_test".parse().unwrap(); - let mut function = Function::new(id, Signature::new([AbiParam::new(Type::I1)], [])); - - let block0 = function.dfg.entry_block(); - let block1 = function.dfg.create_block(); - let block2 = function.dfg.create_block(); - let block3 = function.dfg.create_block(); - let block4 = function.dfg.create_block(); - { - let mut builder = FunctionBuilder::new(&mut function); - let cond = { - let args = builder.block_params(block0); - args[0] - }; - - builder.switch_to_block(block0); - builder.ins().br(block1, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block1); - builder.ins().br(block2, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block2); - builder - .ins() - .cond_br(cond, block1, &[], block3, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block3); - builder - .ins() - .cond_br(cond, block0, &[], block4, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block4); - builder.ins().ret(None, SourceSpan::UNKNOWN); - } - - let cfg = ControlFlowGraph::with_function(&function); - let domtree = DominatorTree::with_function(&function, &cfg); - let loop_analysis = LoopAnalysis::with_function(&function, &cfg, &domtree); - - let loops = loop_analysis.loops().collect::>(); - assert_eq!(loops.len(), 2); - assert_eq!(loop_analysis.loop_header(loops[0]), block0); - assert_eq!(loop_analysis.loop_header(loops[1]), block1); - assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); - assert_eq!(loop_analysis.loop_parent(loops[0]), None); - assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); - assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), true); - assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), true); - assert_eq!(loop_analysis.is_in_loop(block2, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block3, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); - assert_eq!(loop_analysis.loop_level(block0).level(), 1); - assert_eq!(loop_analysis.loop_level(block1).level(), 2); - assert_eq!(loop_analysis.loop_level(block2).level(), 2); - assert_eq!(loop_analysis.loop_level(block3).level(), 1); - } - - #[test] - fn nested_loops_variant2_detection() { - let id = "test::nested_loops_test".parse().unwrap(); - let mut function = Function::new(id, Signature::new([AbiParam::new(Type::I1)], [])); - - let block0 = function.dfg.entry_block(); - let block1 = function.dfg.create_block(); - let block2 = function.dfg.create_block(); - let block3 = function.dfg.create_block(); - let block4 = function.dfg.create_block(); - let block5 = function.dfg.create_block(); - let block6 = function.dfg.create_block(); - let exit = function.dfg.create_block(); - { - let mut builder = FunctionBuilder::new(&mut function); - let cond = { - let args = builder.block_params(block0); - args[0] - }; - - // block0 is outside of any loop - builder.switch_to_block(block0); - builder - .ins() - .cond_br(cond, block1, &[], exit, &[], SourceSpan::UNKNOWN); - - // block1 simply branches to a loop header - builder.switch_to_block(block1); - builder.ins().br(block2, &[], SourceSpan::UNKNOWN); - - // block2 is the outer loop, which is conditionally entered - builder.switch_to_block(block2); - builder - .ins() - .cond_br(cond, block3, &[], exit, &[], SourceSpan::UNKNOWN); - - // block3 simply branches to a nested loop header - builder.switch_to_block(block3); - builder.ins().br(block4, &[], SourceSpan::UNKNOWN); - - // block4 is the inner loop, which is conditionally escaped to the outer loop - builder.switch_to_block(block4); - builder - .ins() - .cond_br(cond, block5, &[], block6, &[], SourceSpan::UNKNOWN); - - // block5 is the loop body of the inner loop - builder.switch_to_block(block5); - builder.ins().br(block4, &[], SourceSpan::UNKNOWN); - - // block6 is a block along the exit edge of the inner loop to the outer loop - builder.switch_to_block(block6); - builder.ins().br(block2, &[], SourceSpan::UNKNOWN); - - // the exit loop leaves the function - builder.switch_to_block(exit); - builder.ins().ret(None, SourceSpan::UNKNOWN); - } - - let cfg = ControlFlowGraph::with_function(&function); - let domtree = DominatorTree::with_function(&function, &cfg); - let loop_analysis = LoopAnalysis::with_function(&function, &cfg, &domtree); - - let loops = loop_analysis.loops().collect::>(); - assert_eq!(loops.len(), 2); - assert_eq!(loop_analysis.loop_header(loops[0]), block2); - assert_eq!(loop_analysis.loop_header(loops[1]), block4); - assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); - assert_eq!(loop_analysis.loop_parent(loops[0]), None); - assert_eq!(loop_analysis.is_in_loop(block0, loops[0]), false); - assert_eq!(loop_analysis.is_in_loop(block0, loops[1]), false); - assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), false); - assert_eq!(loop_analysis.is_in_loop(block1, loops[1]), false); - assert_eq!(loop_analysis.is_in_loop(block2, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block2, loops[1]), false); - assert_eq!(loop_analysis.is_in_loop(block3, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block3, loops[1]), false); - assert_eq!(loop_analysis.is_in_loop(block4, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block4, loops[1]), true); - assert_eq!(loop_analysis.is_in_loop(block5, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block5, loops[1]), true); - assert_eq!(loop_analysis.is_in_loop(block6, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block6, loops[1]), false); - assert!(domtree.dominates(block4, block6, &function.dfg)); - assert_eq!(loop_analysis.is_in_loop(exit, loops[0]), false); - assert_eq!(loop_analysis.is_in_loop(exit, loops[1]), false); - assert_eq!(loop_analysis.loop_level(block0).level(), 0); - assert_eq!(loop_analysis.loop_level(block1).level(), 0); - assert_eq!(loop_analysis.loop_level(block2).level(), 1); - assert_eq!(loop_analysis.loop_level(block3).level(), 1); - assert_eq!(loop_analysis.loop_level(block4).level(), 2); - assert_eq!(loop_analysis.loop_level(block5).level(), 2); - assert_eq!(loop_analysis.loop_level(block6).level(), 1); - } - - #[test] - fn complex_loop_detection() { - let id = "test::complex_loop_test".parse().unwrap(); - let mut function = Function::new(id, Signature::new([AbiParam::new(Type::I1)], [])); - - let entry = function.dfg.entry_block(); - let block1 = function.dfg.create_block(); - let block2 = function.dfg.create_block(); - let block3 = function.dfg.create_block(); - let block4 = function.dfg.create_block(); - let block5 = function.dfg.create_block(); - let block6 = function.dfg.create_block(); - let block7 = function.dfg.create_block(); - { - let mut builder = FunctionBuilder::new(&mut function); - let cond = { - let args = builder.block_params(entry); - args[0] - }; - - builder.switch_to_block(entry); - builder.ins().br(block1, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block1); - builder - .ins() - .cond_br(cond, block2, &[], block4, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block2); - builder.ins().br(block3, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block3); - builder - .ins() - .cond_br(cond, block2, &[], block6, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block4); - builder.ins().br(block5, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block5); - builder - .ins() - .cond_br(cond, block4, &[], block6, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block6); - builder - .ins() - .cond_br(cond, block1, &[], block7, &[], SourceSpan::UNKNOWN); - - builder.switch_to_block(block7); - builder.ins().ret(None, SourceSpan::UNKNOWN); - } - - let cfg = ControlFlowGraph::with_function(&function); - let domtree = DominatorTree::with_function(&function, &cfg); - let loop_analysis = LoopAnalysis::with_function(&function, &cfg, &domtree); - - let loops = loop_analysis.loops().collect::>(); - assert_eq!(loops.len(), 3); - assert_eq!(loop_analysis.loop_header(loops[0]), block1); - assert_eq!(loop_analysis.loop_header(loops[1]), block4); - assert_eq!(loop_analysis.loop_header(loops[2]), block2); - assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); - assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0])); - assert_eq!(loop_analysis.loop_parent(loops[0]), None); - assert_eq!(loop_analysis.is_in_loop(block1, loops[0]), true); - assert_eq!(loop_analysis.is_in_loop(block2, loops[2]), true); - assert_eq!(loop_analysis.is_in_loop(block3, loops[2]), true); - assert_eq!(loop_analysis.is_in_loop(block4, loops[1]), true); - assert_eq!(loop_analysis.is_in_loop(block5, loops[1]), true); - assert_eq!(loop_analysis.is_in_loop(block6, loops[0]), true); - assert_eq!(loop_analysis.loop_level(block1).level(), 1); - assert_eq!(loop_analysis.loop_level(block2).level(), 2); - assert_eq!(loop_analysis.loop_level(block3).level(), 2); - assert_eq!(loop_analysis.loop_level(block4).level(), 2); - assert_eq!(loop_analysis.loop_level(block5).level(), 2); - assert_eq!(loop_analysis.loop_level(block6).level(), 1); - } -} diff --git a/hir-analysis/src/treegraph.rs b/hir-analysis/src/treegraph.rs deleted file mode 100644 index 434620f97..000000000 --- a/hir-analysis/src/treegraph.rs +++ /dev/null @@ -1,786 +0,0 @@ -use std::cmp::Ordering; -use std::collections::{BTreeMap, BTreeSet, VecDeque}; -use std::fmt; - -use smallvec::SmallVec; - -use crate::dependency_graph::*; - -/// [OrderedTreeGraph] represents an immutable, fully-constructed and topologically sorted [TreeGraph]. -/// -/// This is the representation we use during instruction scheduling. -#[derive(Default, Debug)] -pub struct OrderedTreeGraph { - /// The topological order of nodes in `graph` - ordering: Vec, - /// For each tree in `graph`, a data structure which tells us in what order - /// the nodes of that tree will be visited. The smaller the index, the earlier we - /// will emit that node. - indices: BTreeMap, - /// The underlying [TreeGraph] - graph: TreeGraph, -} -impl TryFrom for OrderedTreeGraph { - type Error = UnexpectedCycleError; - - fn try_from(depgraph: DependencyGraph) -> Result { - let graph = TreeGraph::from(depgraph.clone()); - let ordering = graph.toposort()?; - let indices = ordering - .iter() - .copied() - .try_fold(BTreeMap::default(), |mut acc, root| { - acc.insert(root, depgraph.indexed(root)?); - Ok(acc) - })?; - - Ok(Self { - ordering, - indices, - graph, - }) - } -} -impl OrderedTreeGraph { - /// Compute an [OrderedTreeGraph] corresponding to the given [DependencyGraph] - pub fn new(depgraph: &DependencyGraph) -> Result { - Self::try_from(depgraph.clone()) - } - - /// Returns an iterator over nodes in the graph, in topological order. - #[inline] - pub fn iter(&self) -> impl DoubleEndedIterator + '_ { - self.ordering.iter().copied() - } - - /// Returns true if `a` is scheduled before `b` according to the graph - /// - /// See `cmp_scheduling` for details on what scheduling before/after implies. - #[inline] - pub fn is_scheduled_before(&self, a: A, b: B) -> bool - where - A: Into, - B: Into, - { - self.cmp_scheduling(a, b).is_lt() - } - - /// Returns true if `a` is scheduled after `b` according to the graph - /// - /// See `cmp_scheduling` for details on what scheduling before/after implies. - #[inline] - pub fn is_scheduled_after(&self, a: A, b: B) -> bool - where - A: Into, - B: Into, - { - self.cmp_scheduling(a, b).is_gt() - } - - /// Compare two nodes in terms of their scheduling in the graph. - /// - /// If `a` compares less than `b`, then `a` is scheduled before `b`, - /// and vice versa. Two nodes can only compare equal if they are the - /// same node. - /// - /// "Scheduled" here refers to when a node will be visited by the scheduler - /// during its planning phase, which is the opposite order that a given node - /// will be emitted during code generation. This is due to the fact that we visit - /// the dependency graph of a block lazily and bottom-up (i.e. for a given block, - /// we start at the terminator and then materialize any instructions/values - /// referenced by it). - pub fn cmp_scheduling(&self, a: A, b: B) -> Ordering - where - A: Into, - B: Into, - { - let a = a.into(); - let b = b.into(); - if a == b { - return Ordering::Equal; - } - - let a_tree = self.graph.root_id(a); - let b_tree = self.graph.root_id(b); - - // If the nodes reside in the same tree, the precise scheduling - // order is determined by the indexing order for that tree. - if a_tree == b_tree { - let indices = &self.indices[&a_tree]; - let a_idx = indices.get(a).unwrap(); - let b_idx = indices.get(b).unwrap(); - - return a_idx.cmp(&b_idx).reverse(); - } - - // Whichever tree appears first in the topological order will be - // scheduled before the other. - assert!( - !self.ordering.is_empty(), - "invalid treegraph: the topographical ordering is empty even though the underlying graph is not" - ); - for n in self.ordering.iter().copied() { - if n == a_tree { - return Ordering::Less; - } - if n == b_tree { - return Ordering::Greater; - } - } - - unreachable!("invalid treegraph: there are roots in the dependency graph not represented in the topographical ordering") - } -} -impl core::convert::AsRef for OrderedTreeGraph { - #[inline(always)] - fn as_ref(&self) -> &TreeGraph { - &self.graph - } -} -impl core::ops::Deref for OrderedTreeGraph { - type Target = TreeGraph; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.graph - } -} - -/// A [TreeGraph] is used to represent dependencies between expression trees in a program, -/// derived from a [DependencyGraph]. -/// -/// ## What is a TreeGraph? -/// -/// An expression tree is a component of a [DependencyGraph] where each node has at most -/// one predecessor. For example, `(a + b - c) / 2` is an obvious case of such a tree, -/// as each sub-expression produces a single result which is used by the next operator, -/// ultimately producing the final result of the outermost expression. -/// -/// A program can be made up of many such trees, and in some cases the values produced by -/// a tree, or even sub-expressions within the tree, may be used multiple times. -/// To riff on our example above, consider the following: -/// -/// ```text,ignore -/// let d = (a + b - c); -/// let e = d / 2; -/// let f = e * e; -/// d == f -/// ``` -/// -/// This still contains the expression tree from before, but with parts of it reused from -/// another expression tree that, had it duplicated the expressions that are reused, would -/// have formed a larger expression tree. Instead, the reuse results in a forest of two -/// trees, where the "outer" tree depends on the results of the "inner" tree. -/// -/// This is the essence of a [TreeGraph] - it represents a program as a forest of expression -/// trees, just without requiring the program itself to be a tree, resulting in a directed, -/// acyclic graph rather than a forest in the graph-theoretic sense. -/// -/// Nodes in this graph are the roots of the expression trees represented, i.e. each expression -/// tree is condensed into a single node. This is because the graph is largely only concerned with -/// connections between the trees, but we still would be able to answer questions like: -/// -/// * Is a given [Node] a root in the tree graph -/// * If not, what is the root [Node] corresponding to that node -/// * Is a [Node] a member of a given tree -/// * What are the dependencies between trees -/// * What dependencies are condensed in the edge connecting two treegraph nodes -/// -/// The specific way we have implemented [TreeGraph] lets us do all of the above. -/// -/// ## Use Case -/// -/// The [TreeGraph] forms the foundation around which instruction scheduling is performed during -/// code generation. We construct a [DependencyGraph] for each block in a function, and derive -/// a corresponding [TreeGraph]. We then use the reverse topological ordering of the resulting -/// graph as the instruction schedule for that block. -/// -/// Fundamentally though, a [TreeGraph] is a data structure designed to solve the issue of -/// how to efficiently generate code for a stack machine from a non-stack-oriented program -/// representation. It allows one to keep everything on the operand stack, rather than requiring -/// loads/stores to temporaries, and naturally places operands exactly where they are needed, -/// when they are needed. This is a particularly good fit for Miden, because Miden IR is in SSA -/// form, and we need to convert it to efficient Miden Assembly which is a stack machine ISA. -/// -/// ## Additional Reading -/// -/// The treegraph data structure was (to my knowledge) first described in -/// [this paper](https://www.sciencedirect.com/science/article/pii/S1571066111001538?via=ihub) -/// by Park, et al., called _Treegraph-based Instruction Scheduling for Stack-based Virtual Machines_. -/// -/// The implementation and usage of both [DependencyGraph] and [TreeGraph] are based on the design -/// and algorithms described in that paper, so it is worth reading if you are curious about it. -/// Our implementation here is tailored for our use case (i.e. the way we represent nodes has a -/// specific effect on the order in which we visit instructions and their arguments during scheduling), -/// but the overall properties of the data structure described in that paper hold for [TreeGraph] -/// as well. -#[derive(Default, Clone)] -pub struct TreeGraph { - /// The nodes which are explicitly represented in the graph - nodes: BTreeSet, - /// Edges between nodes in the graph, where an edge may carry multiple dependencies - edges: BTreeMap>, - /// A mapping of condensed nodes to the root node of the tree they were condensed into - condensed: BTreeMap, -} - -/// Represents an edge between [TreeGraph] roots. -/// -/// Each pair of nodes in a treegraph may only have one [EdgeId], but -/// multiple [DependencyEdge]s which represents each unique dependency -/// from predecessor root to successor root. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct EdgeId { - /// The treegraph root which is predecessor - predecessor: NodeId, - /// The treegraph root which is successor - successor: NodeId, -} - -/// Represents a unique edge between dependency graph nodes in a [TreeGraph]. -/// -/// The referenced nodes do not have to be represented explicitly in the treegraph as -/// roots, they may also be condensed members of one of the trees in the graph. -/// To help illustrate what I mean, consider an instruction whose result is used -/// twice: once as a block argument to a successor block, and once as an operand -/// to another instruction that is part of an expression tree. The result node will -/// be represented in the treegraph as a root (because it has multiple uses); as will -/// the block terminator (because a terminator can have no uses). However, the last -/// instruction which uses our result is part of an expression tree, so it will not -/// be a root in the treegraph, and instead will be condensed under a root representing -/// the expression tree. Thus we will have two [DependencyEdge] items, one where the -/// predecessor (dependent) is a root; and one where it is not. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct DependencyEdge { - /// The specific node in the dependency graph which is predecessor - predecessor: NodeId, - /// The specific node in the dependency graph which is successor - successor: NodeId, -} - -impl TreeGraph { - /// Returns true if `node` represents a tree in this graph - #[inline(always)] - pub fn is_root(&self, node: impl Into) -> bool { - self.nodes.contains(&node.into()) - } - - /// Return the node representing the root of the tree to which `node` belongs - /// - /// If `node` is the root of its tree, it simply returns itself. - /// - /// NOTE: This function will panic if `node` is not a root OR a node condensed - /// in any tree of the graph. - #[inline] - pub fn root(&self, node: impl Into) -> Node { - self.condensed[&node.into()].into() - } - - /// Same as [TreeGraph::root], but returns the node identifier, which avoids - /// decoding the [NodeId] into a [Node] if not needed. - #[inline] - pub fn root_id(&self, node: impl Into) -> NodeId { - self.condensed[&node.into()] - } - - /// Returns true if `node` is a member of the tree represented by `root` - /// - /// NOTE: This function will panic if `root` is not a tree root - pub fn is_member_of(&self, node: A, root: B) -> bool - where - A: Into, - B: Into, - { - let root = root.into(); - assert!(self.is_root(root)); - self.root(node).id() == root - } - - /// Return the number of times that `node` is referenced as a dependency. - /// - /// Here, `node` can be either explicitly represented in the graph as a tree - /// root, or implicitly, as a condensed node belonging to a tree. - /// - /// For tree roots, the number of dependencies can be different than the - /// number of predecessors, as edges can carry multiple dependencies, and - /// those dependencies can reference the root. However, tree roots by construction - /// must have either zero, or more than one dependent. - /// - /// Condensed nodes on the other hand, by construction have only one dependent, as they - /// belong to a tree; but they may also be referenced by dependencies in the edges - /// between treegraph nodes, so we must check all edges inbound on the tree containing - /// the node for dependencies on `node`. - pub fn num_dependents(&self, node: impl Into) -> usize { - let node = node.into(); - if self.is_root(node) { - self.dependents(node).count() - } else { - self.dependents(node).count() + 1 - } - } - - /// Return an iterator over every node which depends on `node` - pub fn dependents(&self, node: impl Into) -> impl Iterator + '_ { - let dependency_id = node.into(); - let root_id = self.root_id(dependency_id); - self.edges - .iter() - .filter_map(move |(eid, edges)| { - if eid.successor == root_id { - Some(edges.as_slice()) - } else { - None - } - }) - .flat_map(move |edges| { - edges.iter().filter_map(move |e| { - if e.successor == dependency_id { - Some(e.predecessor.into()) - } else { - None - } - }) - }) - } - - /// Return an iterator over each [Dependency] in the edge from `a` to `b` - /// - /// NOTE: This function will panic if either `a` or `b` are not tree roots - pub fn edges(&self, a: NodeId, b: NodeId) -> impl Iterator + '_ { - let id = EdgeId { - predecessor: a, - successor: b, - }; - self.edges[&id].iter().map(|e| Dependency { - dependent: e.predecessor, - dependency: e.successor, - }) - } - - /// Return the number of predecessors for `node` in this graph. - /// - /// NOTE: This function will panic if `node` is not a tree root - pub fn num_predecessors(&self, node: impl Into) -> usize { - let node_id = node.into(); - self.edges.keys().filter(|e| e.successor == node_id).count() - } - - /// Return an iterator over [Node]s which are predecessors of `node` in this graph. - /// - /// NOTE: This function will panic if `node` is not a tree root - pub fn predecessors(&self, node: impl Into) -> impl Iterator + '_ { - let node_id = node.into(); - self.edges.keys().filter_map(move |eid| { - if eid.successor == node_id { - Some(eid.predecessor.into()) - } else { - None - } - }) - } - - /// Return an iterator over [Node]s which are successors of `node` in this graph. - /// - /// NOTE: This function will panic if `node` is not a tree root - pub fn successors(&self, node: impl Into) -> impl Iterator + '_ { - let node_id = node.into(); - self.edges.keys().filter_map(move |eid| { - if eid.predecessor == node_id { - Some(eid.successor.into()) - } else { - None - } - }) - } - - /// Return an iterator over [NodeId]s for successors of `node` in this graph. - /// - /// NOTE: This function will panic if `node` is not a tree root - pub fn successor_ids(&self, node: impl Into) -> impl Iterator + '_ { - let node_id = node.into(); - self.edges.keys().filter_map(move |eid| { - if eid.predecessor == node_id { - Some(eid.successor) - } else { - None - } - }) - } - - /// Remove the edge connecting `a` and `b`. - /// - /// NOTE: This function will panic if either `a` or `b` are not tree roots - pub fn remove_edge(&mut self, a: NodeId, b: NodeId) { - self.edges.remove(&EdgeId { - predecessor: a, - successor: b, - }); - } - - /// Returns a vector of [TreeGraph] roots, sorted in topological order. - /// - /// Returns `Err` if a cycle is detected, making a topological sort impossible. - /// - /// The reverse topological ordering of a [TreeGraph] represents a valid scheduling of - /// the nodes in that graph, as each node is observed before any of it's dependents. - /// - /// Additionally, we ensure that any topological ordering of the graph schedules instruction - /// operands in stack order naturally, i.e. if we emit code from the schedule, there should - /// be little to no stack manipulation required to get instruction operands in the correct - /// order. - /// - /// This is done in the form of an implicit heuristic: the natural ordering for nodes in the - /// graph uses the [Ord] implementation of [NodeId]. When visiting nodes, this order is used, - /// and in the specific case of instruction arguments, which by construction only have a single - /// parent (dependent), they will be visited in this order. In general, this heuristic can be - /// thought of as falling back to the original program order when no other criteria is available - /// for sorting. In reality, it's more of a natural synergy in the data structures representing - /// the graph and nodes in the graph, so it is not nearly so explicit - but the effect is the same. - /// - /// ## Example - /// - /// To better understand how the IR translates to the topological ordering described above, - /// consider the following IR: - /// - /// ```miden-ir,ignore - /// blk0(a, b): - /// c = mul b, b % inst1 - /// d = add a, c % inst2 - /// e = eq.imm d, 0 % inst3 - /// br blk1(d, e, b) % inst4 - /// ``` - /// - /// This code would be a simple expression tree, except we have an instruction result which - /// is used twice in order to pass an intermediate result to the successor block. This is - /// what we'd like to examine in order to determine how such a block will get scheduled. - /// - /// Above we've annotated each instruction in the block with the instruction identifier, which - /// we'll use when referring to those instructions from now on. - /// - /// This IR would get represented in a [DependencyGraph] like so: - /// - /// ```text,ignore - /// inst4 ---------- - /// / \ | - /// v v v - /// arg(0) arg(1) arg(2) - /// | | | - /// | v | - /// | result(e) | - /// | | | - /// | v | - /// | inst3 | - /// v | | - /// result(d)<--- | - /// | | - /// v | - /// inst2 | - /// |_______ | - /// v v | - /// arg(0) arg(1) | - /// | | | - /// v v | - /// stack(a) result(c) | - /// | | - /// v | - /// inst1 | - /// / \ | - /// v v | - /// arg(0) arg(1) | - /// \ / | - /// v v | - /// stack(b)<---- - /// ``` - /// - /// As you can see, arguments and results are explicitly represented in the dependency graph - /// so that we can precisely represent a few key properties: - /// - /// 1. The unique arguments of an instruction - /// 2. The source of an argument (instruction result or stack operand) - /// 3. Which instruction results are used or unused - /// 4. Which instruction results have multiple uses - /// 5. Which values must be copied or moved, and at which points that must happen - /// - /// In any case, the dependency graph above gets translated to the following [TreeGraph]: - /// - /// ```text,ignore - /// inst4 - /// / | - /// v | - /// result(d) | - /// \ | - /// v v - /// stack(b) - /// ``` - /// - /// That might be confusing, but the intuition here is straightforward: the nodes which are - /// explicitly represented in a [TreeGraph] are those nodes in the original [DependencyGraph] - /// with either no dependents, or multiple dependents. Nodes in the original dependency graph - /// with a single dependent are condensed in the [TreeGraph] under whichever ancestor node - /// is a root in the graph. Thus, entire expression trees are collapsed into a single node - /// representing the root of that tree. - /// - /// One thing not shown above, but present in the [TreeGraph] structure, is what information - /// is carried on the edge between treegraph roots, e.g. `inst4` and `result(d)`. - /// In [TreeGraph] terms, the edge simply indicates that the tree represented by `inst4` - /// depends on the tree represented by `result(d)`. However, internally the [TreeGraph] - /// structure also encodes all of the individual dependencies between the two trees that - /// drove the formation of that edge. As a result, we can answer questions such as the number - /// of dependencies on a specific node by finding the treegraph root of the node, and for - /// each predecessor root in the graph, unpacking all of the dependencies along that edge - /// which reference the node in question. - /// - /// So the topological ordering of this graph is simply `[inst4, result(d), stack(b)]`. - /// - /// ## Algorithm - /// - /// The algorithm used to produce the topological ordering is simple: - /// - /// 1. Seed a queue with the set of treegraph roots with no predecessors, enqueuing them - /// in their natural sort order. - /// 2. Pop the next root from the queue, remove all edges between that root and its dependencies, - /// and add the root to the output vector. If any of the dependencies have no remaining - /// predecessors after the aforementioned edge was removed, it is added to the queue. - /// 3. The process in step 2 is repeated until the queue is empty. - /// 4. If there are any edges remaining in the graph, there was a cycle, and thus a - /// topological sort is impossible, this will result in an error being returned. - /// 5. Otherwise, the sort is complete. - /// - /// In effect, a node is only emitted once all of its dependents are emitted, so for codegen, - /// we can use this ordering for instruction scheduling, by visiting nodes in reverse topological - /// order (i.e. visiting dependencies before any of their dependents). This also has the effect - /// of placing items on the stack in the correct order needed for each instruction, as instruction - /// operands will be pushed on the stack right-to-left, so that the first operand to an instruction - /// is on top of the stack. - pub fn toposort(&self) -> Result, UnexpectedCycleError> { - let mut treegraph = self.clone(); - let mut output = Vec::::with_capacity(treegraph.nodes.len()); - let mut roots = treegraph - .nodes - .iter() - .copied() - .filter(|nid| treegraph.num_predecessors(*nid) == 0) - .collect::>(); - - let mut successors = SmallVec::<[NodeId; 4]>::default(); - while let Some(nid) = roots.pop_front() { - output.push(nid); - successors.clear(); - successors.extend(treegraph.successor_ids(nid)); - for mid in successors.drain(..) { - treegraph.remove_edge(nid, mid); - if treegraph.num_predecessors(mid) == 0 { - roots.push_back(mid); - } - } - } - - let has_cycle = treegraph.edges.values().any(|es| !es.is_empty()); - if has_cycle { - Err(UnexpectedCycleError) - } else { - Ok(output) - } - } -} -impl From for TreeGraph { - fn from(mut depgraph: DependencyGraph) -> Self { - let mut cutset = Vec::<(NodeId, NodeId)>::default(); - let mut treegraph = Self::default(); - - // Build cutset - for node_id in depgraph.node_ids() { - let is_multi_use = depgraph.num_predecessors(node_id) > 1; - if is_multi_use { - cutset.extend( - depgraph - .predecessors(node_id) - .map(|d| (d.dependent, d.dependency)), - ); - } - } - - // Apply cutset - for (dependent_id, dependency_id) in cutset.iter() { - depgraph.remove_edge(*dependent_id, *dependency_id); - } - - // Add roots to treegraph - for node_id in depgraph.node_ids() { - if depgraph.num_predecessors(node_id) == 0 { - treegraph.nodes.insert(node_id); - } - } - - // Construct mapping from dependency graph nodes to their - // corresponding treegraph nodes - let mut worklist = VecDeque::::default(); - for root in treegraph.nodes.iter().copied() { - worklist.push_back(root); - while let Some(node) = worklist.pop_front() { - treegraph.condensed.insert(node, root); - for dependency in depgraph.successor_ids(node) { - worklist.push_back(dependency); - } - } - } - - // Add cutset edges to treegraph - for (dependent_id, dependency_id) in cutset.into_iter() { - let a = treegraph.condensed[&dependent_id]; - let b = treegraph.condensed[&dependency_id]; - let edges = treegraph - .edges - .entry(EdgeId { - predecessor: a, - successor: b, - }) - .or_insert_with(Default::default); - let edge = DependencyEdge { - predecessor: dependent_id, - successor: dependency_id, - }; - if edges.contains(&edge) { - continue; - } - edges.push(edge); - } - - treegraph - } -} - -impl fmt::Debug for TreeGraph { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("TreeGraph") - .field("nodes", &DebugNodes(self)) - .field("edges", &DebugEdges(self)) - .finish() - } -} - -struct DebugNodes<'a>(&'a TreeGraph); -impl<'a> fmt::Debug for DebugNodes<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.0.nodes.iter()).finish() - } -} - -struct DebugEdges<'a>(&'a TreeGraph); -impl<'a> fmt::Debug for DebugEdges<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut edges = f.debug_list(); - for EdgeId { - predecessor, - successor, - } in self.0.edges.keys() - { - edges.entry(&format_args!("{predecessor} => {successor}")); - } - edges.finish() - } -} - -#[cfg(test)] -mod tests { - use miden_hir as hir; - - use super::*; - use crate::dependency_graph::simple_dependency_graph; - - /// See [simple_dependency_graph] for details on the input dependency graph. - /// - /// We're expecting to have a treegraph that looks like the following: - /// - /// ```text,ignore - /// inst2 (no predecessors) v3 (no predecessors) - /// | - /// --> v1 (multiply-used) - /// | | - /// | v - /// --> v0 (multiply-used) - /// ``` - /// - /// We expect that in terms of scheduling, trees earlier in the topographical - /// sort of the treegraph are visited earlier during codegen, and within a - /// tree, nodes earlier in the topographical sort of that tree's dependency - /// graph component are visited earlier than other nodes in that tree. - /// - /// For reference, here's the original IR: - /// - /// ```text,ignore - /// block0(v0: i32): - /// v1 = inst0 v0 - /// v3 = inst3 - /// v2 = inst1 v1, v0 - /// inst2 v2, block1(v1), block2(v1, v0) - /// ``` - #[test] - fn treegraph_construction() { - let graph = simple_dependency_graph(); - let treegraph = OrderedTreeGraph::try_from(graph).unwrap(); - - let v0 = hir::Value::from_u32(0); - let v1 = hir::Value::from_u32(1); - let v2 = hir::Value::from_u32(2); - let inst0 = hir::Inst::from_u32(0); - let inst1 = hir::Inst::from_u32(1); - let inst2 = hir::Inst::from_u32(2); - let inst3 = hir::Inst::from_u32(3); - let v0_node = Node::Stack(v0); - let v1_node = Node::Result { - value: v1, - index: 0, - }; - let v2_node = Node::Result { - value: v2, - index: 0, - }; - let inst0_node = Node::Inst { id: inst0, pos: 0 }; - let inst1_node = Node::Inst { id: inst1, pos: 2 }; - let inst2_node = Node::Inst { id: inst2, pos: 3 }; - let inst3_node = Node::Inst { id: inst3, pos: 1 }; - - assert_eq!( - treegraph.cmp_scheduling(inst2_node, inst2_node), - Ordering::Equal - ); - assert_eq!( - treegraph.cmp_scheduling(inst2_node, inst3_node), - Ordering::Less - ); - assert_eq!( - treegraph.cmp_scheduling(inst2_node, inst1_node), - Ordering::Less - ); - assert_eq!( - treegraph.cmp_scheduling(inst1_node, inst2_node), - Ordering::Greater - ); - assert_eq!( - treegraph.cmp_scheduling(inst1_node, inst0_node), - Ordering::Less - ); - assert_eq!( - treegraph.cmp_scheduling(inst1_node, v1_node), - Ordering::Less - ); - assert_eq!( - treegraph.cmp_scheduling(inst0_node, v0_node), - Ordering::Less - ); - - // Instructions must be scheduled before all of their dependencies - assert!(treegraph.is_scheduled_before(inst2_node, inst3_node)); - assert!(treegraph.is_scheduled_before(inst2_node, inst1_node)); - assert!(treegraph.is_scheduled_before(inst2_node, inst0_node)); - assert!(treegraph.is_scheduled_before(inst2_node, v0_node)); - assert!(treegraph.is_scheduled_before(inst2_node, v1_node)); - assert!(treegraph.is_scheduled_before(inst2_node, v2_node)); - assert!(treegraph.is_scheduled_before(inst1_node, v0_node)); - assert!(treegraph.is_scheduled_before(inst1_node, v1_node)); - assert!(treegraph.is_scheduled_before(inst0_node, v0_node)); - // Results are scheduled before instructions which produce them - assert!(treegraph.is_scheduled_before(v2_node, inst1_node)); - } -} diff --git a/hir-analysis/src/validation/block.rs b/hir-analysis/src/validation/block.rs deleted file mode 100644 index 2907af3a1..000000000 --- a/hir-analysis/src/validation/block.rs +++ /dev/null @@ -1,241 +0,0 @@ -use miden_diagnostics::{DiagnosticsHandler, Severity, SourceSpan, Spanned}; -use miden_hir::*; -use rustc_hash::FxHashSet; -use smallvec::SmallVec; - -use super::{Rule, ValidationError}; -use crate::DominatorTree; - -/// This validation rule ensures that all values definitions dominate their uses. -/// -/// For example, it is not valid to use a value in a block when its definition only -/// occurs along a subset of control flow paths which may be taken to that block. -/// -/// This also catches uses of values which are orphaned (i.e. they are defined by -/// a block parameter or instruction which is not attached to the function). -pub struct DefsDominateUses<'a> { - dfg: &'a DataFlowGraph, - domtree: &'a DominatorTree, -} -impl<'a> DefsDominateUses<'a> { - pub fn new(dfg: &'a DataFlowGraph, domtree: &'a DominatorTree) -> Self { - Self { dfg, domtree } - } -} -impl<'a> Rule for DefsDominateUses<'a> { - fn validate( - &mut self, - block_data: &BlockData, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - let current_block = block_data.id; - let mut uses = FxHashSet::::default(); - let mut defs = FxHashSet::::default(); - for node in block_data.insts.iter() { - let span = node.span(); - - uses.clear(); - defs.clear(); - - // Verify the integrity of the instruction results - for result in self.dfg.inst_results(node.key) { - // It should never be possible for a value id to be present in the result set twice - assert!(defs.insert(*result)); - } - - // Gather all value uses to check - 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::MultiDest(ref jts) => { - for jt in jts.iter() { - uses.extend(jt.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." - ); - } - - // Next, ensure that all used values are dominated by their definition - for value in uses.iter().copied() { - match self.dfg.value_data(value) { - // If the value comes from the current block's parameter list, this use is trivially dominated - ValueData::Param { block, .. } if block == ¤t_block => continue, - // If the value comes from another block, then as long as all paths to the current - // block flow through that block, then this use is dominated by its definition - ValueData::Param { block, .. } => { - if self.domtree.dominates(*block, current_block, self.dfg) { - continue; - } - } - // If the value is an instruction result, then as long as all paths to the current - // instruction flow through the defining instruction, then this use is dominated - // by its definition - ValueData::Inst { inst, .. } => { - if self.domtree.dominates(*inst, node.key, self.dfg) { - continue; - } - } - } - - // 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." - ); - } - } - - Ok(()) - } -} - -/// This validation rule ensures that most block-local invariants are upheld: -/// -/// * A block may not be empty -/// * A block must end with a terminator instruction -/// * A block may not contain a terminator instruction in any position but the end -/// * A block which terminates with a branch instruction must reference a block -/// that is present in the function body (i.e. it is not valid to reference -/// detached blocks) -/// * A multi-way branch instruction must have at least one successor -/// * A multi-way branch instruction must not specify the same block as a successor multiple times. -/// -/// This rule does not perform type checking, or verify use/def dominance. -pub struct BlockValidator<'a> { - dfg: &'a DataFlowGraph, - span: SourceSpan, -} -impl<'a> BlockValidator<'a> { - pub fn new(dfg: &'a DataFlowGraph, span: SourceSpan) -> Self { - Self { dfg, span } - } -} -impl<'a> Rule for BlockValidator<'a> { - fn validate( - &mut self, - block_data: &BlockData, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - // Ignore blocks which are not attached to the function body - if !self.dfg.is_block_linked(block_data.id) { - return Ok(()); - } - - // Ensure there is a terminator, and that it is valid - let id = block_data.id; - 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" - ); - } - - let terminator = terminator.unwrap(); - let op = terminator.opcode(); - if !op.is_terminator() { - invalid_block!( - diagnostics, - id, - self.span, - "invalid block terminator", - format!("The last instruction in a block must be a terminator, but {id} ends with {op} which is not a valid terminator") - ); - } - 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!("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") - ); - } - } - 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." - ); - } - - let mut seen = SmallVec::<[Block; 4]>::default(); - for jt in jts.iter() { - let destination = jt.destination; - if !self.dfg.is_block_linked(destination) { - invalid_instruction!( - diagnostics, - terminator.key, - terminator.span(), - "invalid successor", - 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") - ); - } - - if seen.contains(&destination) { - invalid_instruction!( - diagnostics, - terminator.key, - terminator.span(), - "invalid {op} instruction", - 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") - ); - } - - seen.push(destination); - } - } - BranchInfo::NotABranch => (), - } - - // Verify that there are no terminator instructions in any other position than last - 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!("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") - ); - } - } - - Ok(()) - } -} diff --git a/hir-analysis/src/validation/function.rs b/hir-analysis/src/validation/function.rs deleted file mode 100644 index 0e2b87309..000000000 --- a/hir-analysis/src/validation/function.rs +++ /dev/null @@ -1,318 +0,0 @@ -use miden_diagnostics::{DiagnosticsHandler, Severity, Spanned}; -use miden_hir::*; - -use super::{ - BlockValidator, DefsDominateUses, NamingConventions, Rule, TypeCheck, ValidationError, -}; -use crate::{ControlFlowGraph, DominatorTree}; - -/// This validation rule ensures that function-local invariants are upheld: -/// -/// * A function may not be empty -/// * All blocks in the function body must be valid -/// * All uses of values must be dominated by their definitions -/// * All value uses must type check, i.e. branching to a block with a value -/// of a different type than declared by the block parameter is invalid. -pub struct FunctionValidator { - in_kernel_module: bool, -} -impl FunctionValidator { - pub fn new(in_kernel_module: bool) -> Self { - Self { in_kernel_module } - } -} -impl Rule for FunctionValidator { - fn validate( - &mut self, - function: &Function, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - // Validate the function declaration - let mut rules = NamingConventions.chain(CoherentSignature::new(self.in_kernel_module)); - rules.validate(function, diagnostics)?; - - // Ensure basic integrity of the function body - let mut rules = BlockValidator::new(&function.dfg, function.id.span()); - for (_, block) in function.dfg.blocks() { - rules.validate(block, diagnostics)?; - } - - // Construct control flow and dominator tree analyses - let cfg = ControlFlowGraph::with_function(function); - let domtree = DominatorTree::with_function(function, &cfg); - - // Verify value usage - let mut rules = DefsDominateUses::new(&function.dfg, &domtree) - .chain(TypeCheck::new(&function.signature, &function.dfg)); - for (_, block) in function.dfg.blocks() { - rules.validate(block, diagnostics)?; - } - - Ok(()) - } -} - -/// This validation rule ensures that a [Signature] is coherent -/// -/// A signature is coherent if: -/// -/// 1. The linkage is valid for functions -/// 2. The calling convention is valid in the context the function is defined in -/// 3. The ABI of its parameters matches the calling convention -/// 4. The ABI of the parameters and results are coherent, e.g. -/// there are no signed integer parameters which are specified -/// as being zero-extended, there are no results if an sret -/// parameter is present, etc. -struct CoherentSignature { - in_kernel_module: bool, -} -impl CoherentSignature { - pub fn new(in_kernel_module: bool) -> Self { - Self { in_kernel_module } - } -} - -impl Rule for CoherentSignature { - fn validate( - &mut self, - function: &Function, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - 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" - ); - } - - // 2 - let cc = function.signature.cc; - let is_kernel_function = matches!(cc, CallConv::Kernel); - 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" - ); - } 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" - ); - } - } 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" - ); - } - - // 3 - // * sret parameters may not be used with kernel calling convention - // * pointer-typed parameters/results may not be used with kernel calling convention - // * parameters larger than 8 bytes must be passed by reference with fast/C calling conventions - // * results larger than 8 bytes require the use of an sret parameter with fast/C calling conventions - // * total size of all parameters when laid out on the operand stack may not exceed 64 bytes (16 field elements) - // - // 4 - // * paramter count and types must be consistent between the signature and the entry block - // * only sret parameter is permitted, and it must be the first parameter when present - // * the sret attribute may not be applied to results - // * sret parameters imply no results - // * signed integer values may not be combined with zero-extension - // * non-integer values may not be combined with argument extension - let mut sret_count = 0; - 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" - ); - } - for (i, param) in function.signature.params.iter().enumerate() { - let is_first = i == 0; - let value = params[i]; - let span = function.dfg.value_span(value); - let param_ty = ¶m.ty; - 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!( - "The function declares this parameter as having type {param_ty}, \ - but the actual type is {value_ty}" - ) - ); - } - - 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" - ); - } - 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" - ); - } - _ => (), - } - - let is_pointer = param_ty.is_pointer(); - let is_sret = param.purpose == ArgumentPurpose::StructReturn; - if is_sret { - sret_count += 1; - } - - 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" - ); - } - - 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" - ); - } - if !is_pointer { - invalid_function!( - diagnostics, - function.id, - span, - "sret parameters must be pointer-typed, but got {param_ty}", - format!( - "Did you mean to define this parameter with type {}?", - &Type::Ptr(Box::new(param_ty.clone())) - ) - ); - } - - 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." - ); - } - } - - 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!("This parameter has type {param_ty}, you must refactor this function to pass it by reference instead") - ); - } - } - - effective_stack_usage += param_ty - .clone() - .to_raw_parts() - .map(|parts| parts.len()) - .unwrap_or(0); - } - - 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." - ); - } - - 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" - ); - } - - if result.extension != ArgumentExtension::None { - invalid_function!( - diagnostics, - function.id, - "the argument extension attributes are only permitted on function parameters" - ); - } - - 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!("The parameter at index {} has type {}, you must refactor this function to pass it by reference instead", i, &result.ty) - ); - } - } - - Ok(()) - } -} diff --git a/hir-analysis/src/validation/mod.rs b/hir-analysis/src/validation/mod.rs deleted file mode 100644 index bf2be4e9a..000000000 --- a/hir-analysis/src/validation/mod.rs +++ /dev/null @@ -1,487 +0,0 @@ -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; - -pub use self::typecheck::TypeError; - -use miden_diagnostics::{DiagnosticsHandler, SourceSpan}; -use miden_hir::pass::{Analysis, AnalysisManager, AnalysisResult}; - -use miden_hir::*; -use midenc_session::Session; - -use self::block::{BlockValidator, DefsDominateUses}; -use self::function::FunctionValidator; -use self::naming::NamingConventions; -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") - .action(midenc_session::FlagAction::SetFalse) - .help("If present, disables validation of the IR prior to codegen") - .help_heading("Analysis") -} - -/// 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>; - - /// Combine two rules into one rule - fn chain(self, rule: R) -> RuleSet - where - Self: Sized, - R: Rule, - { - RuleSet::new(self, rule) - } -} -impl Rule for &mut R -where - R: Rule, -{ - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - (*self).validate(item, diagnostics) - } -} -impl Rule for Box -where - R: Rule, -{ - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - (**self).validate(item, diagnostics) - } -} -impl Rule for dyn FnMut(&T, &DiagnosticsHandler) -> Result<(), ValidationError> { - #[inline] - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - self(item, diagnostics) - } -} - -/// A [RuleSet] is a combination of multiple rules into a single [Rule] -pub struct RuleSet { - a: A, - b: B, -} -impl RuleSet { - fn new(a: A, b: B) -> Self { - Self { a, b } - } -} -impl Copy for RuleSet -where - A: Copy, - B: Copy, -{ -} -impl Clone for RuleSet -where - A: Clone, - B: Clone, -{ - #[inline] - fn clone(&self) -> Self { - Self::new(self.a.clone(), self.b.clone()) - } -} -impl Rule for RuleSet -where - A: Rule, - B: Rule, -{ - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - self.a - .validate(item, diagnostics) - .and_then(|_| self.b.validate(item, diagnostics)) - } -} - -/// The [ModuleValidationAnalysis] can be used to validate and emit diagnostics for a [Module]. -/// -/// This validates all rules which apply to items at/within module scope. -#[derive(PassInfo)] -pub struct ModuleValidationAnalysis(Result<(), ValidationError>); -impl Analysis for ModuleValidationAnalysis { - type Entity = Module; - - fn analyze( - module: &Self::Entity, - _analyses: &mut AnalysisManager, - session: &Session, - ) -> AnalysisResult { - if session.get_flag("validate") { - 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)), - } - } -} -impl ModuleValidationAnalysis { - fn validate(module: &Module, session: &Session) -> Result<(), ValidationError> { - // Apply module-scoped rules - let mut rules = NamingConventions; - rules.validate(module, &session.diagnostics)?; - - // Apply global-scoped rules - let mut rules = NamingConventions; - for global in module.globals().iter() { - rules.validate(global, &session.diagnostics)?; - } - - // Apply function-scoped rules - let mut rules = FunctionValidator::new(module.is_kernel()); - for function in module.functions() { - rules.validate(function, &session.diagnostics)?; - } - - Ok(()) - } -} -impl From for Result<(), ValidationError> { - fn from(analysis: ModuleValidationAnalysis) -> Self { - analysis.0 - } -} - -#[cfg(test)] -mod tests { - use miden_hir::{ - testing::{self, TestContext}, - ModuleBuilder, - }; - - use super::*; - - #[test] - fn module_validator_test() { - let context = TestContext::default(); - - // Define the 'test' module - let mut builder = ModuleBuilder::new("test"); - builder.with_span(context.current_span()); - testing::sum_matrix(&mut builder, &context); - let module = builder.build(); - - let analysis = ModuleValidationAnalysis::validate(&module, &context.session); - analysis.expect("module was expected to be valid") - } -} diff --git a/hir-analysis/src/validation/naming.rs b/hir-analysis/src/validation/naming.rs deleted file mode 100644 index 477a5520f..000000000 --- a/hir-analysis/src/validation/naming.rs +++ /dev/null @@ -1,258 +0,0 @@ -use miden_diagnostics::{DiagnosticsHandler, Severity, SourceSpan, Spanned}; -use miden_hir::*; - -use super::{Rule, ValidationError}; - -/// This validation rule ensures that all identifiers adhere to the rules of their respective items. -pub struct NamingConventions; -impl Rule for NamingConventions { - fn validate( - &mut self, - module: &Module, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - // Make sure all functions in this module have the same module name in their id - for function in module.functions() { - let id = function.id; - if id.module != module.name { - let expected_name = FunctionIdent { - 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") - ); - } - } - - // 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"); - } - - // 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" - ); - } - - // 3. May otherwise consist of any number of characters of the following classes: - // * `A-Z` - // * `a-z` - // * `0-9` - // * `_-+$@` - // 4. May only contain `:` when used via the namespacing operator, e.g. `std::math` - let mut char_indices = name.char_indices().peekable(); - let mut is_namespaced = false; - while let Some((offset, c)) = char_indices.next() { - match c { - c if c.is_ascii_alphanumeric() => continue, - '_' | '-' | '+' | '$' | '@' => continue, - ':' => match char_indices.peek() { - Some((_, ':')) => { - char_indices.next(); - is_namespaced = true; - 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 '::'?" - ); - } - }, - c if c.is_whitespace() => { - invalid_module!( - diagnostics, - module.name, - "module names may not contain whitespace" - ); - } - 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" - ); - } - } - } - - // 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; - 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); - if component.is_empty() { - invalid_module!( - diagnostics, - module.name, - span, - "submodule names cannot be empty" - ); - } - - if !name.starts_with(is_lower_ascii_alphabetic) { - invalid_module!( - diagnostics, - module.name, - span, - "submodule name must start with a lowercase, ascii-alphabetic character" - ); - } - - offset += len + 2; - } - } - - Ok(()) - } -} -impl Rule for NamingConventions { - fn validate( - &mut self, - function: &Function, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - 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"); - } - - // 2. Must start with an ASCII-alphabetic character, underscore, `$` or `@` - fn name_starts_with(c: char) -> bool { - c.is_ascii_alphabetic() || matches!(c, '_' | '$' | '@') - } - - // 3. Otherwise, no restrictions, but may not contain whitespace - if let Err((offset, c)) = is_valid_identifier(name, name_starts_with, char::is_whitespace) { - if c.is_whitespace() { - let pos = span.start() + offset; - let span = SourceSpan::new(pos, pos); - invalid_function!( - diagnostics, - function.id, - span, - "function names may not contain whitespace" - ); - } 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 '@'"); - } - } - - Ok(()) - } -} -impl Rule for NamingConventions { - fn validate( - &mut self, - global: &GlobalVariableData, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - 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" - ); - } - - // 2. Must start with an ASCII-alphabetic character, underscore, `.`, `$` or `@` - fn name_starts_with(c: char) -> bool { - c.is_ascii_alphabetic() || matches!(c, '_' | '.' | '$' | '@') - } - - // 3. Otherwise, no restrictions, but may not contain whitespace - if let Err((offset, c)) = is_valid_identifier(name, name_starts_with, char::is_whitespace) { - if c.is_whitespace() { - let pos = span.start() + offset; - let span = SourceSpan::new(pos, pos); - invalid_global!( - diagnostics, - global.name, - span, - "global variable names may not contain whitespace" - ); - } 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 '@'"); - } - } - - Ok(()) - } -} - -#[inline(always)] -fn is_lower_ascii_alphabetic(c: char) -> bool { - c.is_ascii_alphabetic() && c.is_ascii_lowercase() -} - -/// This is necessary until [std::str::Pattern] is stabilized -trait Pattern { - fn matches(&self, c: char) -> bool; -} -impl Pattern for char { - #[inline(always)] - fn matches(&self, c: char) -> bool { - *self == c - } -} -impl Pattern for F -where - F: Fn(char) -> bool, -{ - #[inline(always)] - fn matches(&self, c: char) -> bool { - self(c) - } -} - -#[inline] -fn is_valid_identifier(id: &str, start_with: P1, forbidden: P2) -> Result<(), (usize, char)> -where - P1: Pattern, - P2: Pattern, -{ - for (offset, c) in id.char_indices() { - if offset == 0 && !start_with.matches(c) { - return Err((offset, c)); - } - - if forbidden.matches(c) { - return Err((offset, c)); - } - } - - Ok(()) -} diff --git a/hir-analysis/src/validation/typecheck.rs b/hir-analysis/src/validation/typecheck.rs deleted file mode 100644 index 6e0fe66d3..000000000 --- a/hir-analysis/src/validation/typecheck.rs +++ /dev/null @@ -1,1133 +0,0 @@ -use core::fmt; - -use miden_diagnostics::{DiagnosticsHandler, Severity, SourceSpan, Spanned}; -use miden_hir::*; - -use rustc_hash::FxHashMap; - -use super::{Rule, ValidationError}; - -/// This error is produced when type checking the IR for function or module -#[derive(Debug, thiserror::Error, PartialEq, Eq)] -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")] - IncorrectArgumentCount { expected: usize, actual: usize }, - /// The number of results produced does not match what is expected from the instruction - #[error("expected {expected} results, but {actual} are produced")] - IncorrectResultCount { expected: usize, actual: usize }, - /// One of the arguments is not of the correct type - #[error("expected argument of {expected} type at index {index}, got {actual}")] - IncorrectArgumentType { - expected: TypePattern, - actual: Type, - index: usize, - }, - /// One of the results is not of the correct type - #[error("expected result of {expected} type at index {index}, got {actual}")] - InvalidResultType { - expected: TypePattern, - 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}")] - InvalidWideningCast { expected: Type, actual: Type }, - /// An attempt was made to cast from a smaller integer type to a larger one via narrowing cast, e.g. `trunc` - #[error("expected result to be an integral type smaller than {expected}, but got {actual}")] - InvalidNarrowingCast { expected: Type, actual: Type }, - /// The arguments of an instruction were supposed to be the same type, but at least one differs from the controlling type - #[error("expected arguments to be the same type ({expected}), but argument at index {index} is {actual}")] - MatchingArgumentTypeViolation { - expected: Type, - actual: Type, - index: usize, - }, - /// The result type of an instruction was supposed to be the same as the arguments, but it wasn't - #[error("expected result to be the same type ({expected}) as the arguments, but got {actual}")] - MatchingResultTypeViolation { expected: Type, actual: Type }, -} - -/// This validation rule type checks a block to catch any type violations by instructions in that block -pub struct TypeCheck<'a> { - signature: &'a Signature, - dfg: &'a DataFlowGraph, -} -impl<'a> TypeCheck<'a> { - pub fn new(signature: &'a Signature, dfg: &'a DataFlowGraph) -> Self { - Self { signature, dfg } - } -} -impl<'a> Rule for TypeCheck<'a> { - fn validate( - &mut self, - block_data: &BlockData, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { - // Traverse the block, checking each instruction in turn - for node in block_data.insts.iter() { - let span = node.span(); - let opcode = node.opcode(); - let results = self.dfg.inst_results(node.key); - let typechecker = InstTypeChecker::new(diagnostics, self.dfg, node)?; - - match node.as_ref() { - Instruction::UnaryOp(UnaryOp { arg, .. }) => match opcode { - Opcode::ImmI1 - | Opcode::ImmU8 - | Opcode::ImmI8 - | Opcode::ImmU16 - | Opcode::ImmI16 - | Opcode::ImmU32 - | Opcode::ImmI32 - | Opcode::ImmU64 - | Opcode::ImmI64 - | Opcode::ImmFelt - | Opcode::ImmF64 => invalid_instruction!( - diagnostics, - node.key, - span, - "immediate opcode '{opcode}' cannot be used with non-immediate argument" - ), - _ => { - 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" - ), - _ => { - 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"); - } - typechecker.check(&[*addr], results)?; - } - Instruction::BinaryOpImm(BinaryOpImm { imm, arg, .. }) => { - typechecker.check_immediate(&[*arg], imm, results)?; - } - Instruction::PrimOpImm(PrimOpImm { imm, args, .. }) => { - let args = args.as_slice(&self.dfg.value_lists); - typechecker.check_immediate(args, imm, results)?; - } - Instruction::GlobalValue(_) - | Instruction::BinaryOp(_) - | Instruction::PrimOp(_) - | Instruction::Test(_) - | Instruction::InlineAsm(_) - | Instruction::Call(_) => { - let args = node.arguments(&self.dfg.value_lists); - typechecker.check(args, results)?; - } - 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(), - }, - )); - } - 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, - }, - )); - } - } - } - Instruction::RetImm(RetImm { ref arg, .. }) => { - if self.signature.results.len() != 1 { - return Err(ValidationError::TypeError( - TypeError::IncorrectArgumentCount { - expected: self.signature.results.len(), - actual: 1, - }, - )); - } - 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, - }, - )); - } - } - Instruction::Br(Br { - ref args, - destination, - .. - }) => { - 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(), - }, - )); - } - 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(ValidationError::TypeError( - TypeError::IncorrectSuccessorArgumentType { - successor, - expected: expected.clone(), - actual: actual.clone(), - index, - }, - )); - } - } - } - Instruction::CondBr(CondBr { - cond, - then_dest: (then_dest, then_args), - else_dest: (else_dest, else_args), - .. - }) => { - 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); - if args.len() != expected.len() { - return Err(ValidationError::TypeError( - TypeError::IncorrectSuccessorArgumentCount { - successor, - expected: expected.len(), - actual: args.len(), - }, - )); - } - 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(ValidationError::TypeError( - TypeError::IncorrectSuccessorArgumentType { - successor, - expected: expected.clone(), - actual: actual.clone(), - index, - }, - )); - } - } - } - } - Instruction::Switch(Switch { - arg, - arms, - default: fallback, - .. - }) => { - 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 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()) }); - } - } - 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()) }); - } - } - } - } - - Ok(()) - } -} - -/// This type represents a match pattern over kinds of types. -/// -/// This is quite useful in the type checker, as otherwise we would have to handle many -/// type combinations for each instruction. -#[derive(Debug, PartialEq, Eq)] -pub enum TypePattern { - /// Matches any type - Any, - /// Matches any integer type - Int, - /// Matches any unsigned integer type - Uint, - /// Matches any signed integer type - Sint, - /// Matches any pointer type - Pointer, - /// Matches any primitive numeric or pointer type - Primitive, - /// Matches a specific type - Exact(Type), -} -impl TypePattern { - /// Returns true if this pattern matches `ty` - pub fn matches(&self, ty: &Type) -> bool { - match self { - Self::Any => true, - Self::Int => ty.is_integer(), - Self::Uint => ty.is_unsigned_integer(), - Self::Sint => ty.is_signed_integer(), - Self::Pointer => ty.is_pointer(), - Self::Primitive => ty.is_numeric() || ty.is_pointer(), - Self::Exact(expected) => expected.eq(ty), - } - } -} -impl From for TypePattern { - #[inline(always)] - fn from(ty: Type) -> Self { - Self::Exact(ty) - } -} -impl fmt::Display for TypePattern { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Any => f.write_str("any"), - Self::Int => f.write_str("integer"), - Self::Uint => f.write_str("unsigned integer"), - Self::Sint => f.write_str("signed integer"), - Self::Pointer => f.write_str("pointer"), - Self::Primitive => f.write_str("primitive"), - Self::Exact(ty) => write!(f, "{ty}"), - } - } -} - -/// This type represents kinds of instructions in terms of their argument and result types. -/// -/// Each instruction kind represents a category of instructions with similar semantics. -pub enum InstPattern { - /// The instruction matches if it has no arguments or results - Empty, - /// The instruction matches if it has one argument and one result, both of the given type - Unary(TypePattern), - /// The instruction matches if it has one argument of the given type and no results - UnaryNoResult(TypePattern), - /// The instruction matches if it has one argument of the first type and one result of the second type - /// - /// This is used to represent things like `inttoptr` or `ptrtoint` which map one type to another - UnaryMap(TypePattern, TypePattern), - /// The instruction matches if it has one argument of integral type, and one result of a larger integral type - UnaryWideningCast(TypePattern, TypePattern), - /// The instruction matches if it has one argument of integral type, and one result of a smaller integral type - UnaryNarrowingCast(TypePattern, TypePattern), - /// The instruction matches if it has two arguments of the given type, and one result which is the same type as the first argument - Binary(TypePattern, TypePattern), - /// The instruction matches if it has two arguments and one result, all of the same type - BinaryMatching(TypePattern), - /// The instruction matches if it has two arguments of the same type, and no results - BinaryMatchingNoResult(TypePattern), - /// The instruction matches if it has two arguments of the same type, and returns a boolean - BinaryPredicate(TypePattern), - /// The instruction matches if its first argument matches the first type, with two more arguments and one result matching the second type - /// - /// This is used to model instructions like `select` - TernaryMatching(TypePattern, TypePattern), - /// The instruciton matches if it has the exact number of arguments and results given, each corresponding to the given type - Exact(Vec, Vec), - /// The instruction matches any number of arguments and results, of any type - Any, -} -impl InstPattern { - /// Evaluate this pattern against the given arguments and results - pub fn into_match( - self, - dfg: &DataFlowGraph, - args: &[Value], - results: &[Value], - ) -> Result<(), TypeError> { - match self { - Self::Empty => { - if !args.is_empty() { - return Err(TypeError::IncorrectArgumentCount { - expected: 0, - actual: args.len(), - }); - } - if !results.is_empty() { - return Err(TypeError::IncorrectResultCount { - expected: 0, - actual: args.len(), - }); - } - Ok(()) - } - Self::Unary(_) - | Self::UnaryMap(_, _) - | Self::UnaryWideningCast(_, _) - | Self::UnaryNarrowingCast(_, _) => { - if args.len() != 1 { - return Err(TypeError::IncorrectArgumentCount { - expected: 1, - actual: args.len(), - }); - } - if results.len() != 1 { - return Err(TypeError::IncorrectResultCount { - expected: 1, - actual: results.len(), - }); - } - let actual_in = dfg.value_type(args[0]); - let actual_out = dfg.value_type(results[0]); - self.into_unary_match(actual_in, Some(actual_out)) - } - Self::UnaryNoResult(_) => { - if args.len() != 1 { - return Err(TypeError::IncorrectArgumentCount { - expected: 1, - actual: args.len(), - }); - } - if !results.is_empty() { - return Err(TypeError::IncorrectResultCount { - expected: 0, - actual: results.len(), - }); - } - let actual = dfg.value_type(args[0]); - self.into_unary_match(actual, None) - } - Self::Binary(_, _) | Self::BinaryMatching(_) | Self::BinaryPredicate(_) => { - if args.len() != 2 { - return Err(TypeError::IncorrectArgumentCount { - expected: 2, - actual: args.len(), - }); - } - if results.len() != 1 { - return Err(TypeError::IncorrectResultCount { - expected: 1, - actual: results.len(), - }); - } - let lhs = dfg.value_type(args[0]); - let rhs = dfg.value_type(args[1]); - let result = dfg.value_type(results[0]); - self.into_binary_match(lhs, rhs, Some(result)) - } - Self::BinaryMatchingNoResult(_) => { - if args.len() != 2 { - return Err(TypeError::IncorrectArgumentCount { - expected: 2, - actual: args.len(), - }); - } - if !results.is_empty() { - return Err(TypeError::IncorrectResultCount { - expected: 0, - actual: results.len(), - }); - } - let lhs = dfg.value_type(args[0]); - let rhs = dfg.value_type(args[1]); - self.into_binary_match(lhs, rhs, None) - } - Self::TernaryMatching(_, _) => { - if args.len() != 3 { - return Err(TypeError::IncorrectArgumentCount { - expected: 3, - actual: args.len(), - }); - } - if results.len() != 1 { - return Err(TypeError::IncorrectResultCount { - expected: 1, - actual: results.len(), - }); - } - let cond = dfg.value_type(args[0]); - let lhs = dfg.value_type(args[1]); - let rhs = dfg.value_type(args[2]); - let result = dfg.value_type(results[0]); - self.into_ternary_match(cond, lhs, rhs, result) - } - Self::Exact(expected_args, expected_results) => { - if args.len() != expected_args.len() { - return Err(TypeError::IncorrectArgumentCount { - expected: expected_args.len(), - actual: args.len(), - }); - } - if results.len() != expected_results.len() { - return Err(TypeError::IncorrectResultCount { - expected: expected_results.len(), - actual: results.len(), - }); - } - for (index, (expected, arg)) in expected_args - .into_iter() - .zip(args.iter().copied()) - .enumerate() - { - let actual = dfg.value_type(arg); - if !expected.matches(actual) { - return Err(TypeError::IncorrectArgumentType { - expected, - actual: actual.clone(), - index, - }); - } - } - for (index, (expected, result)) in expected_results - .into_iter() - .zip(results.iter().copied()) - .enumerate() - { - let actual = dfg.value_type(result); - if !expected.matches(actual) { - return Err(TypeError::InvalidResultType { - expected, - actual: actual.clone(), - index, - }); - } - } - - Ok(()) - } - Self::Any => Ok(()), - } - } - - /// Evaluate this pattern against the given arguments (including an immediate argument) and results - pub fn into_match_with_immediate( - self, - dfg: &DataFlowGraph, - args: &[Value], - imm: &Immediate, - results: &[Value], - ) -> Result<(), TypeError> { - match self { - Self::Empty => panic!("invalid empty pattern for instruction with immediate argument"), - Self::Unary(_) - | Self::UnaryMap(_, _) - | Self::UnaryWideningCast(_, _) - | Self::UnaryNarrowingCast(_, _) => { - if !args.is_empty() { - return Err(TypeError::IncorrectArgumentCount { - expected: 1, - actual: args.len() + 1, - }); - } - if results.len() != 1 { - return Err(TypeError::IncorrectResultCount { - expected: 1, - actual: results.len(), - }); - } - let actual_in = imm.ty(); - let actual_out = dfg.value_type(results[0]); - self.into_unary_match(&actual_in, Some(actual_out)) - } - Self::UnaryNoResult(_) => { - if !args.is_empty() { - return Err(TypeError::IncorrectArgumentCount { - expected: 1, - actual: args.len() + 1, - }); - } - if !results.is_empty() { - return Err(TypeError::IncorrectResultCount { - expected: 0, - actual: results.len(), - }); - } - let actual = imm.ty(); - self.into_unary_match(&actual, None) - } - Self::Binary(_, _) | Self::BinaryMatching(_) | Self::BinaryPredicate(_) => { - if args.len() != 1 { - return Err(TypeError::IncorrectArgumentCount { - expected: 2, - actual: args.len() + 1, - }); - } - if results.len() != 1 { - return Err(TypeError::IncorrectResultCount { - expected: 1, - actual: results.len(), - }); - } - let lhs = dfg.value_type(args[0]); - let rhs = imm.ty(); - let result = dfg.value_type(results[0]); - self.into_binary_match(lhs, &rhs, Some(result)) - } - Self::BinaryMatchingNoResult(_) => { - if args.len() != 1 { - return Err(TypeError::IncorrectArgumentCount { - expected: 2, - actual: args.len() + 1, - }); - } - if !results.is_empty() { - return Err(TypeError::IncorrectResultCount { - expected: 0, - actual: results.len(), - }); - } - let lhs = dfg.value_type(args[0]); - let rhs = imm.ty(); - self.into_binary_match(lhs, &rhs, None) - } - Self::TernaryMatching(_, _) => { - if args.len() != 2 { - return Err(TypeError::IncorrectArgumentCount { - expected: 3, - actual: args.len() + 1, - }); - } - if results.len() != 1 { - return Err(TypeError::IncorrectResultCount { - expected: 1, - actual: results.len(), - }); - } - let cond = dfg.value_type(args[0]); - let lhs = dfg.value_type(args[1]); - let rhs = imm.ty(); - let result = dfg.value_type(results[0]); - self.into_ternary_match(cond, lhs, &rhs, result) - } - Self::Exact(expected_args, expected_results) => { - if args.len() != expected_args.len() { - return Err(TypeError::IncorrectArgumentCount { - expected: expected_args.len(), - actual: args.len(), - }); - } - if results.len() != expected_results.len() { - return Err(TypeError::IncorrectResultCount { - expected: expected_results.len(), - actual: results.len(), - }); - } - for (index, (expected, arg)) in expected_args - .into_iter() - .zip(args.iter().copied()) - .enumerate() - { - let actual = dfg.value_type(arg); - if !expected.matches(actual) { - return Err(TypeError::IncorrectArgumentType { - expected, - actual: actual.clone(), - index, - }); - } - } - for (index, (expected, result)) in expected_results - .into_iter() - .zip(results.iter().copied()) - .enumerate() - { - let actual = dfg.value_type(result); - if !expected.matches(actual) { - return Err(TypeError::InvalidResultType { - expected, - actual: actual.clone(), - index, - }); - } - } - - Ok(()) - } - Self::Any => Ok(()), - } - } - - fn into_unary_match( - self, - actual_in: &Type, - actual_out: Option<&Type>, - ) -> Result<(), TypeError> { - match self { - Self::Unary(expected) | Self::UnaryNoResult(expected) => { - if !expected.matches(actual_in) { - return Err(TypeError::IncorrectArgumentType { - expected, - actual: actual_in.clone(), - index: 0, - }); - } - if let Some(actual_out) = actual_out { - if actual_in != actual_out { - return Err(TypeError::MatchingResultTypeViolation { - expected: actual_in.clone(), - actual: actual_out.clone(), - }); - } - } - } - Self::UnaryMap(expected_in, expected_out) => { - if !expected_in.matches(actual_in) { - return Err(TypeError::IncorrectArgumentType { - expected: expected_in, - actual: actual_in.clone(), - index: 0, - }); - } - let actual_out = actual_out.expect("expected result type"); - if !expected_out.matches(actual_out) { - return Err(TypeError::InvalidResultType { - expected: expected_out, - actual: actual_out.clone(), - index: 0, - }); - } - } - Self::UnaryWideningCast(expected_in, expected_out) => { - if !expected_in.matches(actual_in) { - return Err(TypeError::IncorrectArgumentType { - expected: expected_in, - actual: actual_in.clone(), - index: 0, - }); - } - let actual_out = actual_out.expect("expected result type"); - if !expected_out.matches(actual_out) { - return Err(TypeError::InvalidResultType { - expected: expected_out, - actual: actual_out.clone(), - index: 0, - }); - } - if actual_in.size_in_bits() > actual_out.size_in_bits() { - return Err(TypeError::InvalidWideningCast { - expected: actual_in.clone(), - actual: actual_out.clone(), - }); - } - } - Self::UnaryNarrowingCast(expected_in, expected_out) => { - if !expected_in.matches(actual_in) { - return Err(TypeError::IncorrectArgumentType { - expected: expected_in, - actual: actual_in.clone(), - index: 0, - }); - } - let actual_out = actual_out.expect("expected result type"); - if !expected_out.matches(actual_out) { - return Err(TypeError::InvalidResultType { - expected: expected_out, - actual: actual_out.clone(), - index: 0, - }); - } - if actual_in.size_in_bits() < actual_out.size_in_bits() { - return Err(TypeError::InvalidNarrowingCast { - expected: actual_in.clone(), - actual: actual_out.clone(), - }); - } - } - Self::Empty - | Self::Binary(_, _) - | Self::BinaryMatching(_) - | Self::BinaryMatchingNoResult(_) - | Self::BinaryPredicate(_) - | Self::TernaryMatching(_, _) - | Self::Exact(_, _) - | Self::Any => unreachable!(), - } - - Ok(()) - } - - fn into_binary_match( - self, - lhs: &Type, - rhs: &Type, - result: Option<&Type>, - ) -> Result<(), TypeError> { - match self { - Self::Binary(expected_lhs, expected_rhs) => { - if !expected_lhs.matches(lhs) { - return Err(TypeError::IncorrectArgumentType { - expected: expected_lhs, - actual: lhs.clone(), - index: 0, - }); - } - if !expected_rhs.matches(rhs) { - return Err(TypeError::IncorrectArgumentType { - expected: expected_rhs, - actual: rhs.clone(), - index: 1, - }); - } - let result = result.expect("expected result type"); - if lhs != result { - return Err(TypeError::MatchingResultTypeViolation { - expected: lhs.clone(), - actual: result.clone(), - }); - } - } - Self::BinaryMatching(expected) | Self::BinaryMatchingNoResult(expected) => { - if !expected.matches(lhs) { - return Err(TypeError::IncorrectArgumentType { - expected, - actual: lhs.clone(), - index: 0, - }); - } - if lhs != rhs { - return Err(TypeError::MatchingArgumentTypeViolation { - expected: lhs.clone(), - actual: rhs.clone(), - index: 1, - }); - } - if let Some(result) = result { - if lhs != result { - return Err(TypeError::MatchingResultTypeViolation { - expected: lhs.clone(), - actual: result.clone(), - }); - } - } - } - Self::BinaryPredicate(expected) => { - if !expected.matches(lhs) { - return Err(TypeError::IncorrectArgumentType { - expected, - actual: lhs.clone(), - index: 0, - }); - } - if lhs != rhs { - return Err(TypeError::MatchingArgumentTypeViolation { - expected: lhs.clone(), - actual: rhs.clone(), - index: 1, - }); - } - let result = result.expect("expected result type"); - let expected = Type::I1; - if result != &expected { - return Err(TypeError::MatchingResultTypeViolation { - expected, - actual: result.clone(), - }); - } - } - Self::Empty - | Self::Unary(_) - | Self::UnaryNoResult(_) - | Self::UnaryMap(_, _) - | Self::UnaryWideningCast(_, _) - | Self::UnaryNarrowingCast(_, _) - | Self::TernaryMatching(_, _) - | Self::Exact(_, _) - | Self::Any => unreachable!(), - } - - Ok(()) - } - - fn into_ternary_match( - self, - cond: &Type, - lhs: &Type, - rhs: &Type, - result: &Type, - ) -> Result<(), TypeError> { - match self { - Self::TernaryMatching(expected_cond, expected_inout) => { - if !expected_cond.matches(cond) { - return Err(TypeError::IncorrectArgumentType { - expected: expected_cond, - actual: cond.clone(), - index: 0, - }); - } - if !expected_inout.matches(lhs) { - return Err(TypeError::IncorrectArgumentType { - expected: expected_inout, - actual: lhs.clone(), - index: 1, - }); - } - if lhs != rhs { - return Err(TypeError::IncorrectArgumentType { - expected: lhs.clone().into(), - actual: rhs.clone(), - index: 2, - }); - } - if lhs != result { - return Err(TypeError::MatchingResultTypeViolation { - expected: lhs.clone(), - actual: result.clone(), - }); - } - } - Self::Empty - | Self::Unary(_) - | Self::UnaryNoResult(_) - | Self::UnaryMap(_, _) - | Self::UnaryWideningCast(_, _) - | Self::UnaryNarrowingCast(_, _) - | Self::Binary(_, _) - | Self::BinaryMatching(_) - | Self::BinaryMatchingNoResult(_) - | Self::BinaryPredicate(_) - | Self::Exact(_, _) - | Self::Any => unreachable!(), - } - - Ok(()) - } -} - -/// This type plays the role of type checking instructions. -/// -/// It is separate from the [TypeCheck] rule itself to factor out -/// all the instruction-related boilerplate. -struct InstTypeChecker<'a> { - diagnostics: &'a DiagnosticsHandler, - dfg: &'a DataFlowGraph, - span: SourceSpan, - opcode: Opcode, - pattern: InstPattern, -} -impl<'a> InstTypeChecker<'a> { - /// Create a new instance of the type checker for the instruction represented by `node`. - pub fn new( - diagnostics: &'a DiagnosticsHandler, - dfg: &'a DataFlowGraph, - node: &InstNode, - ) -> Result { - let span = node.span(); - let opcode = node.opcode(); - let pattern = match opcode { - Opcode::Assert | Opcode::Assertz => InstPattern::UnaryNoResult(Type::I1.into()), - Opcode::AssertEq => InstPattern::BinaryMatchingNoResult(Type::I1.into()), - Opcode::ImmI1 => InstPattern::Unary(Type::I1.into()), - Opcode::ImmU8 => InstPattern::Unary(Type::U8.into()), - Opcode::ImmI8 => InstPattern::Unary(Type::I8.into()), - Opcode::ImmU16 => InstPattern::Unary(Type::U16.into()), - Opcode::ImmI16 => InstPattern::Unary(Type::I16.into()), - Opcode::ImmU32 => InstPattern::Unary(Type::U32.into()), - Opcode::ImmI32 => InstPattern::Unary(Type::I32.into()), - Opcode::ImmU64 => InstPattern::Unary(Type::U64.into()), - Opcode::ImmI64 => InstPattern::Unary(Type::I64.into()), - Opcode::ImmFelt => InstPattern::Unary(Type::Felt.into()), - Opcode::ImmF64 => InstPattern::Unary(Type::F64.into()), - Opcode::Alloca => InstPattern::Exact(vec![], vec![TypePattern::Pointer]), - Opcode::MemGrow => InstPattern::Unary(Type::U32.into()), - opcode @ Opcode::GlobalValue => match node.as_ref() { - Instruction::GlobalValue(GlobalValueOp { global, .. }) => { - match dfg.global_value(*global) { - GlobalValueData::Symbol { .. } | GlobalValueData::IAddImm { .. } => { - InstPattern::Exact(vec![], vec![TypePattern::Pointer]) - } - GlobalValueData::Load { ref ty, .. } => { - InstPattern::Exact(vec![], vec![ty.clone().into()]) - } - } - } - inst => panic!("invalid opcode '{opcode}' for {inst:#?}"), - }, - Opcode::Load => InstPattern::UnaryMap(TypePattern::Pointer, TypePattern::Any), - Opcode::Store => { - InstPattern::Exact(vec![TypePattern::Pointer, TypePattern::Any], vec![]) - } - Opcode::MemCpy => InstPattern::Exact( - vec![TypePattern::Pointer, TypePattern::Pointer, Type::U32.into()], - vec![], - ), - Opcode::PtrToInt => InstPattern::UnaryMap(TypePattern::Pointer, TypePattern::Int), - Opcode::IntToPtr => InstPattern::UnaryMap(TypePattern::Uint, TypePattern::Pointer), - Opcode::Cast => InstPattern::UnaryMap(TypePattern::Int, TypePattern::Int), - Opcode::Trunc => InstPattern::UnaryNarrowingCast(TypePattern::Int, TypePattern::Int), - Opcode::Zext => InstPattern::UnaryWideningCast(TypePattern::Int, TypePattern::Uint), - Opcode::Sext => InstPattern::UnaryWideningCast(TypePattern::Int, TypePattern::Int), - Opcode::Test => InstPattern::UnaryMap(TypePattern::Int, Type::I1.into()), - Opcode::Select => InstPattern::TernaryMatching(Type::I1.into(), TypePattern::Primitive), - Opcode::Add - | Opcode::Sub - | Opcode::Mul - | Opcode::Div - | Opcode::Mod - | Opcode::DivMod - | Opcode::Band - | Opcode::Bor - | Opcode::Bxor => InstPattern::BinaryMatching(TypePattern::Int), - Opcode::Exp | Opcode::Shl | Opcode::Shr | Opcode::Rotl | Opcode::Rotr => { - InstPattern::Binary(TypePattern::Int, TypePattern::Uint) - } - Opcode::Neg - | Opcode::Inv - | Opcode::Incr - | Opcode::Pow2 - | Opcode::Bnot - | Opcode::Popcnt => InstPattern::Unary(TypePattern::Int), - Opcode::Not => InstPattern::Unary(Type::I1.into()), - Opcode::And | Opcode::Or | Opcode::Xor => InstPattern::BinaryMatching(Type::I1.into()), - Opcode::Eq | Opcode::Neq => InstPattern::BinaryPredicate(TypePattern::Primitive), - Opcode::Gt | Opcode::Gte | Opcode::Lt | Opcode::Lte => { - InstPattern::BinaryPredicate(TypePattern::Int) - } - Opcode::IsOdd => InstPattern::Exact(vec![TypePattern::Int], vec![Type::I1.into()]), - Opcode::Min | Opcode::Max => InstPattern::BinaryMatching(TypePattern::Int), - Opcode::Call | Opcode::Syscall => match node.as_ref() { - Instruction::Call(Call { ref callee, .. }) => { - if let Some(import) = dfg.get_import(callee) { - let args = import - .signature - .params - .iter() - .map(|p| TypePattern::Exact(p.ty.clone())) - .collect(); - let results = import - .signature - .results - .iter() - .map(|p| TypePattern::Exact(p.ty.clone())) - .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." - ); - } - } - inst => panic!("invalid opcode '{opcode}' for {inst:#?}"), - }, - Opcode::Br => InstPattern::Any, - Opcode::CondBr => InstPattern::Exact(vec![Type::I1.into()], vec![]), - Opcode::Switch => InstPattern::Exact(vec![Type::U32.into()], vec![]), - Opcode::Ret => InstPattern::Any, - Opcode::Unreachable => InstPattern::Empty, - Opcode::InlineAsm => InstPattern::Any, - }; - Ok(Self { - 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> { - 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)) - } - } - } - - /// Checks that the given `operands` (with immediate) and `results` match the types represented by this [InstTypeChecker] - pub fn check_immediate( - self, - operands: &[Value], - imm: &Immediate, - results: &[Value], - ) -> Result<(), ValidationError> { - 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)) - } - } - } -} diff --git a/hir-macros/Cargo.toml b/hir-macros/Cargo.toml deleted file mode 100644 index d76680ac0..000000000 --- a/hir-macros/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "miden-hir-macros" -description = "Provides proc macro support for Miden IR" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true -publish.workspace = true - -[lib] -proc-macro = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -Inflector.workspace = true -proc-macro2 = "1.0" -quote = "1.0" - -[dependencies.syn] -version = "2.0" -features = ["full", "parsing", "derive"] diff --git a/hir-macros/src/lib.rs b/hir-macros/src/lib.rs deleted file mode 100644 index 8c934064d..000000000 --- a/hir-macros/src/lib.rs +++ /dev/null @@ -1,239 +0,0 @@ -use inflector::cases::kebabcase::to_kebab_case; -use quote::quote; -use syn::spanned::Spanned; -use syn::{parse_macro_input, DeriveInput, Ident, Token}; - -#[proc_macro_derive(PassInfo)] -pub fn derive_pass_info(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - let derive_input = parse_macro_input!(item as DeriveInput); - let derive_span = derive_input.span(); - let id = derive_input.ident.clone(); - let generics = derive_input.generics; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let name = derive_input.ident.to_string(); - let pass_name = to_kebab_case(&name); - let pass_name_lit = syn::Lit::Str(syn::LitStr::new(&pass_name, id.span())); - - let doc_ident = syn::Ident::new("doc", derive_span); - let docs = derive_input - .attrs - .iter() - .filter_map(|attr| match attr.meta { - syn::Meta::NameValue(ref nv) => { - if nv.path.get_ident()? == &doc_ident { - match nv.value { - syn::Expr::Lit(syn::ExprLit { - lit: syn::Lit::Str(ref lit), - .. - }) => Some(lit.value()), - _ => None, - } - } else { - None - } - } - syn::Meta::Path(_) | syn::Meta::List(_) => None, - }) - .collect::>(); - let pass_summary = match docs.first() { - Some(line) => syn::Lit::Str(syn::LitStr::new(line, derive_span)), - None => syn::Lit::Str(syn::LitStr::new("", derive_span)), - }; - let description = docs.into_iter().collect::(); - let pass_description = syn::Lit::Str(syn::LitStr::new(&description, derive_span)); - - let quoted = quote! { - impl #impl_generics PassInfo for #id #ty_generics #where_clause { - const FLAG: &'static str = #pass_name_lit; - const SUMMARY: &'static str = #pass_summary; - const DESCRIPTION: &'static str = #pass_description; - } - }; - - proc_macro::TokenStream::from(quoted) -} - -#[proc_macro_derive(AnalysisKey, attributes(analysis_key))] -pub fn derive_analysis_key(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - let derive_input = parse_macro_input!(item as DeriveInput); - let derive_span = derive_input.span(); - let id = derive_input.ident.clone(); - let generics = derive_input.generics; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let found = match &derive_input.data { - syn::Data::Struct(ref data) => match &data.fields { - syn::Fields::Named(ref fields) => { - let mut found = None; - for field in fields.named.iter() { - if field.attrs.iter().any(is_analysis_key_attr) { - if found.is_some() { - return syn::Error::new( - field.span(), - "duplicate #[analysis_key] field", - ) - .into_compile_error() - .into(); - } - found = Some((field.ident.as_ref().cloned().unwrap(), field.ty.clone())); - } - } - found - } - syn::Fields::Unnamed(ref fields) => { - let mut found = None; - for (i, field) in fields.unnamed.iter().enumerate() { - if field.attrs.iter().any(is_analysis_key_attr) { - if found.is_some() { - return syn::Error::new( - field.span(), - "duplicate #[analysis_key] field", - ) - .into_compile_error() - .into(); - } - found = Some((Ident::new(&i.to_string(), field.span()), field.ty.clone())); - } - } - found - } - syn::Fields::Unit => { - return syn::Error::new( - derive_span, - "structs with unit fields cannot derive AnalysisKey", - ) - .into_compile_error() - .into() - } - }, - syn::Data::Enum(_) => { - return syn::Error::new(derive_span, "enums cannot derive AnalysisKey") - .into_compile_error() - .into() - } - syn::Data::Union(_) => { - return syn::Error::new(derive_span, "unions cannot derive AnalysisKey") - .into_compile_error() - .into() - } - }; - - let (field_id, field_ty) = match found { - Some(found) => found, - None => { - return syn::Error::new(derive_span, "missing #[analysis_key] attribute") - .into_compile_error() - .into() - } - }; - - let quoted = quote! { - impl #impl_generics AnalysisKey for #id #ty_generics #where_clause { - type Key = #field_ty; - - fn key(&self) -> Self::Key { self.#field_id } - } - }; - - proc_macro::TokenStream::from(quoted) -} - -#[proc_macro_derive(RewritePassRegistration)] -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 quoted = quote! { - inventory::submit!(miden_hir::pass::RewritePassRegistration::new::<#id>()); - }; - - proc_macro::TokenStream::from(quoted) -} - -#[proc_macro_derive(ModuleRewritePassAdapter)] -pub fn derive_module_rewrite_pass_adapter( - item: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let derive_input = parse_macro_input!(item as DeriveInput); - let id = derive_input.ident.clone(); - - let quoted = quote! { - inventory::submit!(miden_hir::pass::RewritePassRegistration::new::>()); - }; - - proc_macro::TokenStream::from(quoted) -} - -#[proc_macro_derive(ConversionPassRegistration)] -pub fn derive_conversion_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 ConversionPassRegistration 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 ConversionPassRegistration 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 ConversionPassRegistration on a generic type with const arguments") - .into_compile_error() - .into(); - } - } - } - - let quoted = if params.empty_or_trailing() { - quote! { - inventory::submit! { - midenc_session::CompileFlag::new(<#id as PassInfo>::FLAG) - .long(<#id as PassInfo>::FLAG) - .help(<#id as PassInfo>::SUMMARY) - .help_heading("Conversions") - .action(midenc_session::FlagAction::SetTrue) - } - } - } else { - quote! { - 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("Conversions") - .action(midenc_session::FlagAction::SetTrue) - } - } - }; - - proc_macro::TokenStream::from(quoted) -} - -fn is_analysis_key_attr(attr: &syn::Attribute) -> bool { - if let syn::Meta::Path(ref path) = attr.meta { - path.is_ident("analysis_key") - } else { - false - } -} diff --git a/hir-symbol/Cargo.toml b/hir-symbol/Cargo.toml deleted file mode 100644 index 79bef0601..000000000 --- a/hir-symbol/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "miden-hir-symbol" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] - -[build-dependencies] -Inflector.workspace = true -rustc-hash.workspace = true -toml.workspace = true diff --git a/hir-symbol/build.rs b/hir-symbol/build.rs deleted file mode 100644 index 38bfbd484..000000000 --- a/hir-symbol/build.rs +++ /dev/null @@ -1,194 +0,0 @@ -extern crate inflector; -extern crate rustc_hash; -extern crate toml; - -use std::cmp::Ordering; -use std::collections::BTreeSet; -use std::env; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::path::{Path, PathBuf}; - -use inflector::Inflector; -use rustc_hash::FxHashSet; -use toml::{value::Table, Value}; - -#[derive(Debug, Default, Clone)] -struct Symbol { - key: String, - id: Option, - value: String, - is_keyword: bool, -} -impl Eq for Symbol {} -impl PartialEq for Symbol { - fn eq(&self, other: &Self) -> bool { - self.value == other.value - } -} -impl PartialOrd for Symbol { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for Symbol { - fn cmp(&self, other: &Self) -> Ordering { - // Ensure that we always consider symbols with the same value equivalent - if self.value == other.value { - return Ordering::Equal; - } - - // Otherwise, sort by id first, then value - match (self.id, other.id) { - (None, None) => self.value.cmp(&other.value), - (Some(_), None) => Ordering::Less, - (Some(x), Some(y)) => x.cmp(&y), - (None, Some(_)) => Ordering::Greater, - } - } -} -impl Symbol { - fn from_value>(name: S, value: &Value) -> Self { - let name = name.into(); - let table = value.as_table().unwrap(); - let id = table - .get("id") - .map(|id| id.as_integer().expect("id must be an integer")); - let value = match table - .get("value") - .map(|v| v.as_str().expect("value must be a string")) - { - None => name.clone(), - Some(value) => value.to_string(), - }; - // When the name is, e.g. UPPER_CASE, keep it that way rather than transforming it - // as the casing is intentional - let key = if name.is_screaming_snake_case() { - name - } else { - name.to_pascal_case() - }; - Self { - key, - id, - value, - is_keyword: false, - } - } -} - -struct Section { - name: String, - keys: BTreeSet, -} -impl Section { - fn new(name: String) -> Self { - Self { - name, - keys: BTreeSet::new(), - } - } - - fn from_table(name: String, table: &Table) -> Self { - let mut section = Section::new(name); - for (name, value) in table.iter() { - let mut sym = Symbol::from_value(name, value); - sym.is_keyword = section.name == "keywords"; - assert!(section.keys.insert(sym), "duplicate symbol {}", name); - } - section - } - - fn iter(&self) -> impl Iterator { - self.keys.iter() - } -} - -fn main() { - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let out_file = out_dir.join("symbols.rs"); - - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=src/symbols.toml"); - println!("cargo:rustc-env=SYMBOLS_RS={}", out_file.display()); - - let contents = fs::read_to_string("src/symbols.toml").unwrap(); - let root = contents.parse::().unwrap(); - let root = root.as_table().unwrap(); - let mut sections = vec![]; - for (name, value) in root.iter() { - sections.push(Section::from_table( - name.to_string(), - value.as_table().unwrap(), - )); - } - - let mut reserved = FxHashSet::default(); - for section in sections.iter() { - for symbol in section.iter() { - if let Some(id) = symbol.id { - assert!( - reserved.insert(id), - "duplicate symbol id {} in section {}", - id, - §ion.name - ); - } - } - } - - let mut symbols = vec![]; - sections.drain(..).fold(0, |next_id, section| { - section.keys.iter().fold(next_id, |mut next_id, symbol| { - let mut symbol = symbol.clone(); - while reserved.contains(&next_id) { - next_id += 1; - } - if symbol.id.is_none() { - symbol.id = Some(next_id); - next_id += 1 - } - symbols.push(symbol); - next_id - }) - }); - - generate_symbols_rs(&out_file, symbols).unwrap(); -} - -fn generate_symbols_rs(path: &Path, symbols: Vec) -> std::io::Result<()> { - let mut file = File::create(path)?; - - // Symbol declarations - for symbol in symbols.iter() { - let key = &symbol.key; - let id = symbol.id.unwrap(); - writeln!(&mut file, "#[allow(non_upper_case_globals)]")?; - writeln!( - &mut file, - "pub const {key}: crate::Symbol = crate::Symbol::new({id});" - )? - } - - // Symbol strings - file.write_all(b"\n\npub(crate) const __SYMBOLS: &[(crate::Symbol, &str)] = &[\n")?; - for symbol in symbols.iter() { - let key = &symbol.key; - let value = &symbol.value; - writeln!(&mut file, " ({key}, \"{value}\"),")?; - } - file.write_all(b"];\n\n")?; - - // fn is_keyword(sym: Symbol) -> bool - file.write_all(b"pub fn is_keyword(sym: crate::Symbol) -> bool {\n")?; - file.write_all(b" #[allow(non_upper_case_globals, clippy::match_like_matches_macro)]\n")?; - file.write_all(b" match sym {\n")?; - for symbol in symbols.iter().filter(|s| s.is_keyword) { - let key = &symbol.key; - writeln!(&mut file, " {key} => true,")?; - } - file.write_all(b" _ => false,\n")?; - file.write_all(b" }\n}\n\n")?; - - Ok(()) -} diff --git a/hir-symbol/src/lib.rs b/hir-symbol/src/lib.rs deleted file mode 100644 index 8befefcef..000000000 --- a/hir-symbol/src/lib.rs +++ /dev/null @@ -1,177 +0,0 @@ -use core::fmt; -use core::mem; -use core::ops::Deref; -use core::str; - -use std::collections::BTreeMap; -use std::sync::{OnceLock, RwLock}; - -static SYMBOL_TABLE: OnceLock = OnceLock::new(); - -pub mod symbols { - include!(env!("SYMBOLS_RS")); -} - -struct SymbolTable { - interner: RwLock, -} -impl SymbolTable { - pub fn new() -> Self { - Self { - interner: RwLock::new(Interner::new()), - } - } -} -unsafe impl Sync for SymbolTable {} - -/// A symbol is an interned string. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct Symbol(SymbolIndex); - -impl Symbol { - #[inline] - pub const fn new(n: u32) -> Self { - Self(SymbolIndex::new(n)) - } - - /// Maps a string to its interned representation. - pub fn intern>(string: S) -> Self { - let string = string.into(); - with_interner(|interner| interner.intern(string)) - } - - pub fn as_str(self) -> &'static str { - with_read_only_interner(|interner| unsafe { - // This is safe because the interned string will live for the - // lifetime of the program - mem::transmute::<&str, &'static str>(interner.get(self)) - }) - } - - #[inline] - pub fn as_u32(self) -> u32 { - self.0.as_u32() - } - - #[inline] - pub fn as_usize(self) -> usize { - self.0.as_usize() - } - - /// Returns true if this symbol is a keyword in the IR textual format - #[inline] - pub fn is_keyword(self) -> bool { - symbols::is_keyword(self) - } -} -impl fmt::Debug for Symbol { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}({:?})", self, self.0) - } -} -impl fmt::Display for Symbol { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.as_str(), f) - } -} -impl PartialOrd for Symbol { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for Symbol { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.as_str().cmp(other.as_str()) - } -} -impl> PartialEq for Symbol { - fn eq(&self, other: &T) -> bool { - self.as_str() == other.deref() - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct SymbolIndex(u32); -impl SymbolIndex { - // shave off 256 indices at the end to allow space for packing these indices into enums - pub const MAX_AS_U32: u32 = 0xFFFF_FF00; - - #[inline] - const fn new(n: u32) -> Self { - assert!(n <= Self::MAX_AS_U32, "out of range value used"); - - SymbolIndex(n) - } - - #[inline] - pub fn as_u32(self) -> u32 { - self.0 - } - - #[inline] - pub fn as_usize(self) -> usize { - self.0 as usize - } -} -impl From for u32 { - #[inline] - fn from(v: SymbolIndex) -> u32 { - v.as_u32() - } -} -impl From for usize { - #[inline] - fn from(v: SymbolIndex) -> usize { - v.as_usize() - } -} - -#[derive(Default)] -struct Interner { - pub names: BTreeMap<&'static str, Symbol>, - pub strings: Vec<&'static str>, -} - -impl Interner { - pub fn new() -> Self { - let mut this = Interner::default(); - for (sym, s) in symbols::__SYMBOLS { - this.names.insert(s, *sym); - this.strings.push(s); - } - this - } - - pub fn intern(&mut self, string: String) -> Symbol { - if let Some(&name) = self.names.get(string.as_str()) { - return name; - } - - let name = Symbol::new(self.strings.len() as u32); - - let string = string.into_boxed_str(); - let string: &'static str = Box::leak(string); - self.strings.push(string); - self.names.insert(string, name); - name - } - - pub fn get(&self, symbol: Symbol) -> &str { - self.strings[symbol.0.as_usize()] - } -} - -// If an interner exists, return it. Otherwise, prepare a fresh one. -#[inline] -fn with_interner T>(f: F) -> T { - let table = SYMBOL_TABLE.get_or_init(SymbolTable::new); - let mut r = table.interner.write().unwrap(); - f(&mut r) -} - -#[inline] -fn with_read_only_interner T>(f: F) -> T { - let table = SYMBOL_TABLE.get_or_init(SymbolTable::new); - let r = table.interner.read().unwrap(); - f(&r) -} diff --git a/hir-symbol/src/symbols.toml b/hir-symbol/src/symbols.toml deleted file mode 100644 index bababf11f..000000000 --- a/hir-symbol/src/symbols.toml +++ /dev/null @@ -1,90 +0,0 @@ -[fundamental] -false = { id = 0 } -true = { id = 1 } -empty = { value = "" } - -[keywords] -kernel = {} -module = {} -internal = {} -odr = {} -external = {} -extern = {} -pub = {} -fn = {} -cc = {} -fast = {} -sret = {} -zext = {} -sext = {} -trunc = {} -ret = {} -call = {} -syscall = {} -br = {} -condbr = {} -switch = {} -test = {} -load = {} -memcpy = {} -asm = {} -min = {} -max = {} -exp = {} -and = {} -band = {} -or = {} -bor = {} -xor = {} -bxor = {} -eq = {} -neq = {} -gt = {} -gte = {} -lt = {} -lte = {} -store = {} -inv = {} -pow2 = {} -not = {} -bnot = {} -popcnt = {} -is_odd = { value = "is_odd" } -cast = {} -ptrtoint = {} -inttoptr = {} -neg = {} -select = {} -assert = {} -assertz = {} -alloca = {} -unreachable = {} -i1 = {} -i8 = {} -u8 = {} -i16 = {} -u16 = {} -i32 = {} -u32 = {} -i64 = {} -u64 = {} -i128 = {} -u128 = {} -u256 = {} -f64 = {} -felt = {} -mut = {} -as = {} -id = {} -global = {} -symbol = {} -iadd = {} -const = {} - -[passes] -inline_blocks = { value = "inline-blocks" } -split_critical_edges = { value = "split-critical-edges" } -treeify = { value = "treeify" } - -[attributes] -entrypoint = {} diff --git a/hir-transform/Cargo.toml b/hir-transform/Cargo.toml deleted file mode 100644 index b3de95ab5..000000000 --- a/hir-transform/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "miden-hir-transform" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -anyhow.workspace = true -inventory.workspace = true -miden-diagnostics.workspace = true -miden-hir.workspace = true -miden-hir-analysis.workspace = true -midenc-session.workspace = true -rustc-hash.workspace = true -smallvec.workspace = true - -[dev-dependencies] -pretty_assertions.workspace = true diff --git a/hir-transform/src/adt/mod.rs b/hir-transform/src/adt/mod.rs deleted file mode 100644 index 38aa84015..000000000 --- a/hir-transform/src/adt/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod scoped_map; - -pub use self::scoped_map::ScopedMap; diff --git a/hir-transform/src/adt/scoped_map.rs b/hir-transform/src/adt/scoped_map.rs deleted file mode 100644 index 2f996fb6d..000000000 --- a/hir-transform/src/adt/scoped_map.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::borrow::Borrow; -use std::fmt; -use std::hash::Hash; -use std::rc::Rc; - -use rustc_hash::FxHashMap; - -#[derive(Clone)] -pub struct ScopedMap -where - K: Eq + Hash, -{ - parent: Option>>, - map: FxHashMap, -} -impl Default for ScopedMap -where - K: Eq + Hash, -{ - fn default() -> Self { - Self { - parent: None, - map: Default::default(), - } - } -} -impl ScopedMap -where - K: Eq + Hash, -{ - pub fn new(parent: Option>>) -> Self { - Self { - parent, - map: Default::default(), - } - } - - pub fn get(&self, k: &Q) -> Option<&V> - where - K: Borrow, - Q: Hash + Eq + ?Sized, - { - self.map - .get(k) - .or_else(|| self.parent.as_ref().and_then(|p| p.get(k))) - } - - pub fn insert(&mut self, k: K, v: V) { - self.map.insert(k, v); - } - - pub fn extend(&mut self, iter: I) - where - I: IntoIterator, - { - self.map.extend(iter); - } -} -impl fmt::Debug for ScopedMap -where - K: Eq + Hash + fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ScopedMap") - .field("parent", &self.parent) - .field("map", &self.map) - .finish() - } -} diff --git a/hir-transform/src/inline_blocks.rs b/hir-transform/src/inline_blocks.rs deleted file mode 100644 index 1a623c5fa..000000000 --- a/hir-transform/src/inline_blocks.rs +++ /dev/null @@ -1,480 +0,0 @@ -use std::collections::VecDeque; - -use rustc_hash::FxHashSet; -use smallvec::SmallVec; - -use miden_hir::pass::{AnalysisManager, RewritePass, RewriteResult}; -use miden_hir::{self as hir, *}; -use miden_hir_analysis::ControlFlowGraph; -use midenc_session::Session; - -use crate::adt::ScopedMap; - -/// This pass inlines unnecessary blocks, and removes unnecessary block arguments. -/// -/// Specifically, the blocks affected are those with a single predecessor, as they -/// by definition can be inlined into their immediate predecessor in such cases. -/// -/// Blocks like this may have been introduced for the following reasons: -/// -/// * Due to less than optimal lowering to SSA form -/// * To split critical edges in preparation for dataflow analysis and related transformations, -/// but ultimately no code introduced along those edges, and critical edges no longer present -/// an obstacle to further optimization or codegen. -/// * During treeification of the CFG, where blocks with multiple predecessors were duplicated -/// to produce a CFG in tree form, where no blocks (other than loop headers) have multiple -/// predecessors. This process removed block arguments from these blocks, and rewrote instructions -/// dominated by those block arguments to reference the values passed from the original predecessor -/// to whom the subtree is attached. This transformation can expose a chain of blocks which all have -/// a single predecessor and successor, introducing branches where none are needed, and by removing -/// those redundant branches, all of the code from blocks in the chain can be inlined in the first -/// block of the chain. -#[derive(Default, PassInfo, ModuleRewritePassAdapter)] -pub struct InlineBlocks; -impl RewritePass for InlineBlocks { - type Entity = hir::Function; - - fn apply( - &mut self, - function: &mut Self::Entity, - analyses: &mut AnalysisManager, - _session: &Session, - ) -> RewriteResult { - let mut cfg = analyses - .take::(&function.id) - .unwrap_or_else(|| ControlFlowGraph::with_function(function)); - - let entry = function.dfg.entry_block(); - let mut changed = false; - let mut rewrites = ScopedMap::::default(); - let mut visited = FxHashSet::::default(); - let mut worklist = VecDeque::::default(); - worklist.push_back(entry); - - // First, search down the CFG for non-loop header blocks with only a single successor. - // These blocks form possible roots of a chain of blocks that can be inlined. - // - // For each such root, we then check if the successor block has a single predecessor, - // if so, then we can remove the terminator instruction from the root block, and then - // move all of the code from the successor block into the root block. We can then repeat - // this process until we inline a terminator instruction that is not an unconditional branch - // to a single successor. - while let Some(p) = worklist.pop_front() { - // If we've already visited a block, skip it - if !visited.insert(p) { - continue; - } - - // If the block is detached, then it has already been inlined, - // so we do not need to run inlining on it, however we should - // visit its successors anyway, as there may be inlining - // opportunities later in the CFG. - let is_detached = !function.dfg.is_block_linked(p); - - // If this block has multiple successors, or multiple predecessors, add all of it's - // successors to the work queue and move on. - if is_detached || cfg.num_successors(p) > 1 || cfg.num_predecessors(p) > 1 { - for b in cfg.succ_iter(p) { - worklist.push_back(b); - } - continue; - } - - // This block is a candidate for inlining - // - // 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()) - { - // 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); - break; - } - - // Only inline if the successor has no block arguments - // - // TODO: We can inline blocks with arguments as well, but with higher cost, - // 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() { - // Compute the set of values to rewrite - for (from, to) in function - .dfg - .block_params(b) - .iter() - .copied() - .zip(args.iter().copied()) - { - rewrites.insert(from, to); - } - } - - inline(b, p, function, &mut worklist, &rewrites, &mut cfg); - - // Mark that the control flow graph as modified - changed = true; - } - } - - rewrite_uses(entry, function, &rewrites); - - analyses.insert(function.id, cfg); - if !changed { - analyses.mark_preserved::(&function.id); - } - - Ok(()) - } -} - -fn inline( - from: Block, - to: Block, - function: &mut hir::Function, - worklist: &mut VecDeque, - rewrites: &ScopedMap, - cfg: &mut ControlFlowGraph, -) { - assert_ne!(from, to); - let mut from_terminator = None; - { - let mut from_insts = function.dfg.block_mut(from).insts.take(); - let to_insts = &mut function.dfg.blocks[to].insts; - // Remove the original terminator - to_insts.pop_back(); - // Move all instructions from their original block to the parent, - // updating the instruction data along the way to reflect the change - // in location - while let Some(unsafe_ix_ref) = from_insts.pop_front() { - let mut ix = unsafe { UnsafeRef::into_box(unsafe_ix_ref) }; - ix.block = to; - rewrite_use(ix.as_mut(), &mut function.dfg.value_lists, rewrites); - // We need to clone the original terminator so we can continue to - // navigate the control flow graph - if ix.opcode().is_terminator() { - let replacement = Box::new(ix.deep_clone(&mut function.dfg.value_lists)); - assert!( - from_terminator.replace(replacement).is_none(), - "a block can only have one terminator" - ); - } - to_insts.push_back(UnsafeRef::from_box(ix)); - } - } - // 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::CondBr(CondBr { - then_dest: (then_blk, _), - else_dest: (else_blk, _), - .. - }) => { - worklist.push_back(*then_blk); - worklist.push_back(*else_blk); - } - _ => (), - } - function.dfg.blocks[from] - .insts - .push_back(UnsafeRef::from_box(from_terminator)); - // Detach the original block from the function - function.dfg.detach_block(from); - // Update the control flow graph to reflect the changes - cfg.detach_block(from); - cfg.recompute_block(&function.dfg, to); -} - -fn rewrite_uses(root: Block, function: &mut hir::Function, rewrites: &ScopedMap) { - let mut visited = FxHashSet::::default(); - let mut worklist = VecDeque::::default(); - worklist.push_back(root); - - while let Some(b) = worklist.pop_front() { - // Do not visit the same block twice - if !visited.insert(b) { - continue; - } - - let block = &mut function.dfg.blocks[b]; - // Take the list of instructions away from the block to simplify traversing the block - let mut insts = block.insts.take(); - // Take each instruction out of the list, top to bottom, modify it, then - // add it back to the instruction list of the block directly. This ensures - // we traverse the list and rewrite instructions in a single pass without - // any additional overhead - while let Some(inst) = insts.pop_front() { - let mut inst = unsafe { UnsafeRef::into_box(inst) }; - let to_visit = rewrite_use(inst.as_mut(), &mut function.dfg.value_lists, rewrites); - if !to_visit.is_empty() { - worklist.extend(to_visit); - } - - block.insts.push_back(UnsafeRef::from_box(inst)); - } - } -} - -fn rewrite_use( - inst: &mut Instruction, - pool: &mut hir::ValueListPool, - rewrites: &ScopedMap, -) -> SmallVec<[Block; 2]> { - let mut worklist = SmallVec::<[Block; 2]>::default(); - match inst { - Instruction::Br(Br { - destination, - ref mut args, - .. - }) => { - worklist.push(*destination); - for arg in args.as_mut_slice(pool) { - if let Some(replacement) = rewrites.get(arg).copied() { - *arg = replacement; - } - } - } - Instruction::CondBr(CondBr { - ref mut cond, - then_dest: (then_dest, ref mut then_args), - else_dest: (else_dest, ref mut else_args), - .. - }) => { - worklist.push(*then_dest); - worklist.push(*else_dest); - if let Some(replacement) = rewrites.get(cond).copied() { - *cond = replacement; - } - for arg in then_args.as_mut_slice(pool) { - if let Some(replacement) = rewrites.get(arg).copied() { - *arg = replacement; - } - } - for arg in else_args.as_mut_slice(pool) { - if let Some(replacement) = rewrites.get(arg).copied() { - *arg = replacement; - } - } - } - op => { - for arg in op.arguments_mut(pool) { - if let Some(replacement) = rewrites.get(arg).copied() { - *arg = replacement; - } - } - } - } - - worklist -} - -#[cfg(test)] -mod tests { - use miden_hir::{ - pass::{AnalysisManager, RewritePass}, - testing::TestContext, - AbiParam, Function, FunctionBuilder, Immediate, InstBuilder, Signature, SourceSpan, Type, - }; - use pretty_assertions::{assert_eq, assert_ne}; - - use crate::InlineBlocks; - - /// Run the inlining pass on the following IR: - /// - /// The following IR is unnecessarily verbose: - /// - /// ```text,ignore - /// pub fn test(*mut u8, i32) -> *mut u8 { - /// entry(ptr0: *mut u8, offset: i32): - /// zero = const.u32 0; - /// ptr1 = ptrtoint ptr0 : u32; - /// is_null = eq ptr1, zero; - /// br blk0(ptr1, is_null); - /// - /// blk0(ptr2: u32, is_null1: i1): - /// condbr is_null1, blk2(ptr0), blk1(ptr2); - /// - /// blk1(ptr3: u32): - /// ptr4 = add ptr3, offset; - /// is_null2 = eq ptr4, zero; - /// condbr is_null2, blk4(ptr0), blk5(ptr4); - /// - /// blk2(result0: *mut u8): - /// br blk3; - /// - /// blk3: - /// ret result0; - /// - /// blk4(result1: *mut u8): - /// ret result1; - /// - /// blk5(ptr5: u32): - /// ptr6 = inttoptr ptr5 : *mut u8; - /// ret ptr6; - /// } - /// ``` - /// - /// We want the inlining pass to result in something like: - /// - /// ```text,ignore - /// pub fn test(*mut u8, i32) -> *mut u8 { - /// entry(ptr0: *mut u8, offset: i32): - /// zero = const.u32 0; - /// ptr1 = ptrtoint ptr0 : u32; - /// is_null = eq ptr1, zero; - /// condbr is_null, blk2, blk1; - /// - /// blk1: - /// ptr2 = add ptr1, offset; - /// is_null1 = eq ptr2, zero; - /// condbr is_null1, blk3, blk4; - /// - /// blk2: - /// ret ptr0; - /// - /// blk3: - /// ret ptr0; - /// - /// blk4: - /// ptr3 = inttoptr ptr2 : *mut u8; - /// ret ptr3; - /// } - /// ``` - /// - /// In short, regardless of block arguments, control flow edges between blocks - /// where the predecessor is the only predecessor, and the successor is the only - /// successor, represent edges which can be removed by inlining the successor - /// block into the predecessor block. Any uses of values introduced as block - /// parameters of the successor block, must be rewritten to use the values - /// provided in the predecessor block for those parameters. - #[test] - fn inline_blocks_simple_tree_cfg_test() { - let context = TestContext::default(); - let id = "test::inlining_test".parse().unwrap(); - let mut function = Function::new( - id, - Signature::new( - [ - AbiParam::new(Type::Ptr(Box::new(Type::U8))), - AbiParam::new(Type::I32), - ], - [AbiParam::new(Type::Ptr(Box::new(Type::U8)))], - ), - ); - - { - let mut builder = FunctionBuilder::new(&mut function); - let entry = builder.current_block(); - let (ptr0, offset) = { - let args = builder.block_params(entry); - (args[0], args[1]) - }; - - let a = builder.create_block(); // blk0(ptr2: u32, is_null1: i1) - let ptr2 = builder.append_block_param(a, Type::U32, SourceSpan::UNKNOWN); - let is_null1 = builder.append_block_param(a, Type::I1, SourceSpan::UNKNOWN); - let b = builder.create_block(); // blk1(ptr3: u32) - let ptr3 = builder.append_block_param(b, Type::U32, SourceSpan::UNKNOWN); - let c = builder.create_block(); // blk2(result0: *mut u8) - let result0 = - builder.append_block_param(c, Type::Ptr(Box::new(Type::U8)), SourceSpan::UNKNOWN); - let d = builder.create_block(); // blk3 - let e = builder.create_block(); // blk4(result1: *mut u8) - let result1 = - builder.append_block_param(e, Type::Ptr(Box::new(Type::U8)), SourceSpan::UNKNOWN); - let f = builder.create_block(); // blk5(ptr5: u32) - let ptr5 = builder.append_block_param(f, Type::U32, SourceSpan::UNKNOWN); - - // entry - let ptr1 = builder.ins().ptrtoint(ptr0, Type::U32, SourceSpan::UNKNOWN); - let is_null = builder - .ins() - .eq_imm(ptr1, Immediate::U32(0), SourceSpan::UNKNOWN); - builder.ins().br(a, &[ptr1, is_null], SourceSpan::UNKNOWN); - - // blk0 - builder.switch_to_block(a); - builder - .ins() - .cond_br(is_null1, c, &[ptr0], b, &[ptr2], SourceSpan::UNKNOWN); - - // blk1 - builder.switch_to_block(b); - let ptr3_i32 = builder.ins().cast(ptr3, Type::I32, SourceSpan::UNKNOWN); - let ptr4_i32 = builder - .ins() - .add_checked(ptr3_i32, offset, SourceSpan::UNKNOWN); - let ptr4 = builder.ins().cast(ptr4_i32, Type::U32, SourceSpan::UNKNOWN); - let is_null2 = builder - .ins() - .eq_imm(ptr4, Immediate::U32(0), SourceSpan::UNKNOWN); - builder - .ins() - .cond_br(is_null2, e, &[ptr0], f, &[ptr4], SourceSpan::UNKNOWN); - - // blk2 - builder.switch_to_block(c); - builder.ins().br(d, &[], SourceSpan::UNKNOWN); - - // blk3 - builder.switch_to_block(d); - builder.ins().ret(Some(result0), SourceSpan::UNKNOWN); - - // blk4 - builder.switch_to_block(e); - builder.ins().ret(Some(result1), SourceSpan::UNKNOWN); - - // blk5 - builder.switch_to_block(f); - let ptr6 = - builder - .ins() - .inttoptr(ptr5, Type::Ptr(Box::new(Type::U8)), SourceSpan::UNKNOWN); - builder.ins().ret(Some(ptr6), SourceSpan::UNKNOWN); - } - - let original = function.to_string(); - let mut analyses = AnalysisManager::default(); - let mut rewrite = InlineBlocks; - rewrite - .apply(&mut function, &mut analyses, &context.session) - .expect("inlining failed"); - - let expected = "pub fn inlining_test(*mut u8, i32) -> *mut u8 { -block0(v0: *mut u8, v1: i32): - v8 = ptrtoint v0 : u32; - v9 = eq v8, 0 : i1; - condbr v9, block3(v0), block2(v8); - -block2(v4: u32): - v10 = cast v4 : i32; - v11 = add.checked v10, v1 : i32; - v12 = cast v11 : u32; - v13 = eq v12, 0 : i1; - condbr v13, block5(v0), block6(v12); - -block3(v5: *mut u8): - ret v5; - -block5(v6: *mut u8): - ret v6; - -block6(v7: u32): - v14 = inttoptr v7 : *mut u8; - ret v14; -} -"; - - let inlined = function.to_string(); - assert_ne!(inlined, original); - assert_eq!(inlined.as_str(), expected); - } -} diff --git a/hir-transform/src/lib.rs b/hir-transform/src/lib.rs deleted file mode 100644 index b257f8fbd..000000000 --- a/hir-transform/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub(crate) mod adt; -mod inline_blocks; -mod split_critical_edges; -mod treeify; - -pub use self::inline_blocks::InlineBlocks; -pub use self::split_critical_edges::SplitCriticalEdges; -pub use self::treeify::Treeify; diff --git a/hir-transform/src/split_critical_edges.rs b/hir-transform/src/split_critical_edges.rs deleted file mode 100644 index 4efa9b6d4..000000000 --- a/hir-transform/src/split_critical_edges.rs +++ /dev/null @@ -1,291 +0,0 @@ -use std::collections::VecDeque; - -use rustc_hash::FxHashSet; -use smallvec::SmallVec; - -use miden_hir::pass::{AnalysisManager, RewritePass, RewriteResult}; -use miden_hir::{self as hir, Block as BlockId, *}; -use miden_hir_analysis::ControlFlowGraph; -use midenc_session::Session; - -/// This pass breaks any critical edges in the CFG of a function. -/// -/// A critical edge occurs when control flow may exit a block, which we'll call `P`, to -/// more than one successor block, which we'll call `S`, where any `S` has more than one -/// predecessor from which it may receive control. Put another way, in the control flow graph, -/// a critical edge is one which connects two nodes where the source node has multiple outgoing -/// edges, and the destination node has multiple incoming edges. -/// -/// These types of edges cause unnecessary complications with certain types of dataflow analyses -/// and transformations, and so we fix this by splitting these edges. This is done by introducing -/// a new block, `B`, in which we insert a branch to `S` with whatever arguments were originally -/// provided in `P`, and then rewriting the branch in `P` that went to `S`, to go to `B` instead. -/// -/// After this pass completes, no node in the control flow graph will have both multiple predecessors -/// and multiple successors. -/// -#[derive(Default, PassInfo, ModuleRewritePassAdapter)] -pub struct SplitCriticalEdges; -impl RewritePass for SplitCriticalEdges { - type Entity = hir::Function; - - fn apply( - &mut self, - function: &mut Self::Entity, - analyses: &mut AnalysisManager, - _session: &Session, - ) -> RewriteResult { - // Search for blocks with multiple successors with edges to blocks with - // multiple predecessors; these blocks form critical edges in the control - // flow graph which must be split. - // - // We split the critical edge by inserting a new block after the predecessor - // and updating the predecessor instruction to transfer to the new block - // instead. We then insert an unconditional branch in the new block that - // passes the block arguments that were meant for the "real" successor. - let mut visited = FxHashSet::::default(); - let mut worklist = VecDeque::::default(); - worklist.push_back(function.dfg.entry_block()); - - let mut cfg = analyses - .take::(&function.id) - .unwrap_or_else(|| ControlFlowGraph::with_function(function)); - - while let Some(p) = worklist.pop_front() { - // If we've already visited a block, skip it - if !visited.insert(p) { - continue; - } - - // Make sure we visit all of the successors of this block next - for b in cfg.succ_iter(p) { - worklist.push_back(b); - } - - // Unless this block has multiple successors, skip it - if cfg.num_successors(p) < 2 { - continue; - } - - let succs = SmallVec::<[BlockId; 2]>::from_iter(cfg.succ_iter(p)); - for b in succs.into_iter() { - // Unless this successor has multiple predecessors, skip it - if cfg.num_predecessors(b) < 2 { - continue; - } - - // We found a critical edge, so perform the following steps: - // - // * Create a new block, placed after the predecessor in the layout - // * Rewrite the terminator of the predecessor to refer to the new - // block, but without passing any block arguments - // * Insert an unconditional branch to the successor with the block - // arguments of the original terminator - // * Recompute the control flow graph for affected blocks - let split = function.dfg.create_block_after(p); - let terminator = function.dfg.last_inst(p).unwrap(); - let span = function.dfg.inst_span(terminator); - let ix = function.dfg.inst_mut(terminator); - let args: ValueList; - match ix { - Instruction::Br(hir::Br { - ref mut destination, - args: ref mut orig_args, - .. - }) => { - args = orig_args.take(); - *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), - .. - }) => { - if *then_dest == b { - *then_dest = split; - args = then_args.take(); - } else { - *else_dest = split; - args = else_args.take(); - } - } - Instruction::Switch(_) => unimplemented!(), - _ => unreachable!(), - } - function.dfg.insert_inst( - InsertionPoint { - at: ProgramPoint::Block(split), - action: Insert::After, - }, - Instruction::Br(hir::Br { - op: hir::Opcode::Br, - destination: b, - args, - }), - Type::Unknown, - span, - ); - - cfg.recompute_block(&function.dfg, split); - } - - cfg.recompute_block(&function.dfg, p); - } - - analyses.insert(function.id, cfg); - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use miden_hir::{ - pass::{AnalysisManager, RewritePass}, - testing::TestContext, - AbiParam, Function, FunctionBuilder, Immediate, InstBuilder, Signature, SourceSpan, Type, - }; - use pretty_assertions::{assert_eq, assert_ne}; - - use crate::SplitCriticalEdges; - - /// Run the split critical edges pass on the following IR: - /// - /// The following IR is contains a critical edge to split, specifically - /// `blk0` is critical because it has multiple predecessors, and multiple - /// successors: - /// - /// ```text,ignore - /// pub fn test(*mut u8, u32) -> *mut u8 { - /// entry(ptr0: *mut u8, n0: u32): - /// ptr1 = ptrtoint ptr0 : u32; - /// br blk0(ptr1, n0); - /// - /// blk0(ptr2: u32, n1: u32): - /// is_null = eq ptr2, 0; - /// condbr is_null, blk2(ptr0), blk1(ptr2, n1); - /// - /// blk1(ptr3: u32, n2: u32): - /// ptr4 = sub ptr3, n2; - /// n3 = sub n2, 1; - /// is_zero = eq n3, 0; - /// condbr is_zero, blk2(ptr4), blk0(ptr4, n3); - /// - /// blk2(result0: *mut u8) - /// ret result0; - /// } - /// ``` - /// - /// We expect this pass to introduce new blocks along all control flow paths - /// where the successor has multiple predecessors. This may result in some - /// superfluous blocks after the pass is run, but this can be addressed by - /// running the [InlineBlocks] pass afterwards, which will flatten the CFG. - #[test] - fn split_critical_edges_simple_test() { - let context = TestContext::default(); - let id = "test::sce".parse().unwrap(); - let mut function = Function::new( - id, - Signature::new( - [ - AbiParam::new(Type::Ptr(Box::new(Type::U8))), - AbiParam::new(Type::U32), - ], - [AbiParam::new(Type::Ptr(Box::new(Type::U8)))], - ), - ); - - { - let mut builder = FunctionBuilder::new(&mut function); - let entry = builder.current_block(); - let (ptr0, n0) = { - let args = builder.block_params(entry); - (args[0], args[1]) - }; - - let a = builder.create_block(); // blk0(ptr2: u32, n1: u32) - let ptr2 = builder.append_block_param(a, Type::U32, SourceSpan::UNKNOWN); - let n1 = builder.append_block_param(a, Type::U32, SourceSpan::UNKNOWN); - let b = builder.create_block(); // blk1(ptr3: u32, n2: u32) - let ptr3 = builder.append_block_param(b, Type::U32, SourceSpan::UNKNOWN); - let n2 = builder.append_block_param(b, Type::U32, SourceSpan::UNKNOWN); - let c = builder.create_block(); // blk2(result0: u32) - let result0 = builder.append_block_param(c, Type::U32, SourceSpan::UNKNOWN); - - // entry - let ptr1 = builder.ins().ptrtoint(ptr0, Type::U32, SourceSpan::UNKNOWN); - builder.ins().br(a, &[ptr1, n0], SourceSpan::UNKNOWN); - - // blk0 - builder.switch_to_block(a); - let is_null = builder - .ins() - .eq_imm(ptr2, Immediate::U32(0), SourceSpan::UNKNOWN); - builder - .ins() - .cond_br(is_null, c, &[ptr0], b, &[ptr2, n1], SourceSpan::UNKNOWN); - - // blk1 - builder.switch_to_block(b); - let ptr4 = builder.ins().sub_checked(ptr3, n2, SourceSpan::UNKNOWN); - let n3 = builder - .ins() - .sub_imm_checked(n2, Immediate::U32(1), SourceSpan::UNKNOWN); - let is_zero = builder - .ins() - .eq_imm(n3, Immediate::U32(0), SourceSpan::UNKNOWN); - builder - .ins() - .cond_br(is_zero, c, &[ptr4], a, &[ptr4, n3], SourceSpan::UNKNOWN); - - // blk2 - builder.switch_to_block(c); - let result1 = - builder - .ins() - .inttoptr(result0, Type::Ptr(Box::new(Type::U8)), SourceSpan::UNKNOWN); - builder.ins().ret(Some(result1), SourceSpan::UNKNOWN); - } - - let original = function.to_string(); - let mut analyses = AnalysisManager::default(); - let mut rewrite = SplitCriticalEdges; - rewrite - .apply(&mut function, &mut analyses, &context.session) - .expect("splitting critical edges failed"); - - let expected = "pub fn sce(*mut u8, u32) -> *mut u8 { -block0(v0: *mut u8, v1: u32): - v7 = ptrtoint v0 : u32; - br block1(v7, v1); - -block1(v2: u32, v3: u32): - v8 = eq v2, 0 : i1; - condbr v8, block4, block2(v2, v3); - -block4: - br block3(v0); - -block2(v4: u32, v5: u32): - v9 = sub.checked v4, v5 : u32; - v10 = sub.checked v5, 1 : u32; - v11 = eq v10, 0 : i1; - condbr v11, block6, block5; - -block6: - br block3(v9); - -block5: - br block1(v9, v10); - -block3(v6: u32): - v12 = inttoptr v6 : *mut u8; - ret v12; -} -"; - - let transformed = function.to_string(); - assert_ne!(transformed, original); - assert_eq!(transformed.as_str(), expected); - } -} diff --git a/hir-transform/src/treeify.rs b/hir-transform/src/treeify.rs deleted file mode 100644 index b2db2d3c1..000000000 --- a/hir-transform/src/treeify.rs +++ /dev/null @@ -1,761 +0,0 @@ -use std::collections::VecDeque; -use std::rc::Rc; - -use miden_hir::pass::{AnalysisManager, RewritePass, RewriteResult}; -use miden_hir::{self as hir, Block as BlockId, Value as ValueId, *}; -use miden_hir_analysis::{BlockPredecessor, ControlFlowGraph, DominatorTree, LoopAnalysis}; -use midenc_session::Session; -use rustc_hash::FxHashSet; - -use crate::adt::ScopedMap; - -/// This pass rewrites the CFG of a function so that it forms a tree. -/// -/// While we technically call this treeification, loop headers are preserved, so -/// there are still nodes in the CFG with multiple predecessors, but _only_ those -/// blocks which are loop headers are permitted to be unaltered. -/// -/// This transformation splits vertices with multiple predecessors, by duplicating the -/// subtree of the program rooted at those vertices. As mentioned above, we do not split -/// vertices representing loop headers, in order to preserve loops in the CFG of the resulting -/// IR. However, we can consider each loop within the overall CFG of a function to be a single -/// vertex after this transformation, and with this perspective the CFG forms a tree. Loop -/// nodes are then handled specially during codegen. -/// -/// The transformation is performed bottom-up, in CFG postorder. -/// -/// This pass also computes the set of blocks in each loop which must be terminated with `push.0` -/// to exit the containing loop. -/// -/// # Examples -/// -/// ## Basic DAG -/// -/// This example demonstrates how the DAG of a function with multiple returns gets transformed: -/// -/// ```text,ignore -/// blk0 -/// | -/// v -/// blk1 -> blk3 -> ret -/// | / -/// | / -/// | / -/// v v -/// blk2 -/// | -/// v -/// ret -/// ``` -/// -/// Becomes: -/// -/// ```text,ignore -/// blk0 -/// | -/// v -/// blk1 -> blk3 -> ret -/// | | -/// | | -/// | | -/// v v -/// blk2 blk2 -/// | | -/// v v -/// ret ret -/// ``` -/// -/// ## Basic Loop -/// -/// This is an example of a function with multiple returns and a simple loop: -/// -/// ```text,ignore -/// blk0 -/// | ------- -/// v v | -/// blk1 -> blk3 -> blk4 -> blk5 -> ret -/// | / -/// | / -/// | / -/// v v -/// blk2 -/// | -/// v -/// ret -/// ``` -/// -/// Becomes: -/// -/// ```text,ignore -/// blk0 -/// | ------- -/// v v | -/// blk1 -> blk3 -> blk4 -> blk5 -> ret -/// | | -/// | | -/// | | -/// v v -/// blk2 blk2 -/// | | -/// v v -/// ret ret -/// ``` -/// -/// ## Complex Loop -/// -/// This is an example of a function with a complex loop (i.e. multiple exit points): -/// -/// ```text,ignore -/// blk0 -/// | -/// v -/// blk1 -/// | \ -/// | blk2 <----- -/// | | | -/// | blk3 | -/// | / \ | -/// | / blk4-- -/// | / | -/// vv | -/// blk5 blk6 -/// ``` -/// -/// Becomes: -/// -/// ```text,ignore -/// blk0 -/// | -/// v -/// blk1 -/// | \ -/// | \ -/// | blk2 <--- -/// | | | -/// | v | -/// | blk3 | -/// | | \ | -/// | | blk4-- -/// | | | -/// v v v -/// blk5 blk5 blk6 -/// ``` -/// -/// NOTE: Here, when generating code for `blk5` and `blk6`, the loop depth is 0, so -/// we will emit a single `push.0` at the end of both blocks which will terminate the -/// containing loop, and then return from the function as we've reached the bottom -/// of the tree. -/// -/// ## Nested Loops -/// -/// This is an extension of the example above, but with nested loops: -/// -/// ```text,ignore -/// blk0 -/// | -/// v -/// blk1 -/// | \ -/// | blk2 <------- -/// | | | | -/// | blk3 | | -/// | / \ | | -/// | / blk4-- | -/// | / | | -/// vv v | -/// blk5<- blk6-->blk7-->blk8 -/// | ^ | -/// | |_____________| -/// | | -/// |__________________| -/// ``` -/// -/// We have two loops, the outer one starting at `blk2`: -/// -/// * `blk2->blk3->blk4->blk2` -/// * `blk2->blk3->blk4->blk6->blk7->blk2` -/// -/// And the inner one starting at `blk6`: -/// -/// * `blk6->blk7->blk8->blk6` -/// -/// Additionally, there are multiple exits through the loops, depending on the path taken: -/// -/// * `blk2->blk3->blk5` -/// * `blk2->blk3->blk4->blk6->blk7->blk8->blk5` -/// * `blk6->blk7->blk8->blk5` -/// -/// After transformation, this becomes: -/// -/// ```text,ignore -/// blk0 -/// | -/// v -/// blk1 -/// | \ -/// | blk2 <------- -/// | | | | -/// | blk3 | | -/// | | \ | | -/// | | blk4-- | -/// | | | | -/// v v v | -/// blk5 blk5 blk6-->blk7-->blk8 -/// ^ | | -/// |_____________|_| -/// | -/// v -/// blk5 -/// ``` -/// -/// During codegen though, we end up with the following tree of stack machine code. -/// -/// At each point where control flow either continues a loop or leaves it, we must -/// -/// * Duplicate loop headers on control flow edges leading to those headers -/// * Emit N `push.0` instructions on control flow edges exiting the function from a loop depth of N -/// * Emit a combination of the above on control flow edges exiting an inner loop for an outer loop, -/// depending on what depths the predecessor and successor blocks are at -/// -/// ```text,ignore -/// blk0 -/// blk1 -/// if.true -/// blk2 -/// while.true -/// blk3 -/// if.true -/// blk4 -/// if.true -/// blk2 # duplicated outer loop header -/// else -/// blk6 -/// while.true -/// blk7 -/// if.true -/// blk2 # duplicated outer loop header -/// push.0 # break out of inner loop -/// else -/// blk8 -/// if.true -/// blk6 # duplicated inner loop header -/// else -/// blk5 -/// push.0 # break out of outer loop -/// push.0 # break out of inner loop -/// end -/// end -/// end -/// end -/// else -/// blk5 -/// push.0 # break out of outer loop -/// end -/// end -/// else -/// blk5 -/// end -/// ``` -/// -#[derive(Default, PassInfo, ModuleRewritePassAdapter)] -pub struct Treeify; -impl RewritePass for Treeify { - type Entity = hir::Function; - - fn apply( - &mut self, - function: &mut Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> RewriteResult { - let cfg = analyses.get_or_compute::(function, session)?; - let domtree = analyses.get_or_compute::(function, session)?; - let loops = analyses.get_or_compute::(function, session)?; - - let mut block_q = VecDeque::::default(); - let mut changed = false; - - for b in domtree.cfg_postorder().iter().copied() { - if loops.is_loop_header(b).is_some() { - // Ignore loop headers - continue; - } - - // Blocks with multiple predecessors cause the CFG to form a DAG, - // we need to duplicate the CFG rooted at this block for all predecessors. - // - // While we could technically preserve one of the predecessors, we perform - // some transformations during the copy that would result in copied vs original - // trees to differ slightly, which would inhibit subsequent optimizations. - // The original subtree blocks are detached from the function. - if cfg.num_predecessors(b) > 1 { - for p in cfg.pred_iter(b) { - assert!(block_q.is_empty()); - block_q.push_back(CopyBlock::new(b, p)); - while let Some(CopyBlock { - b, - ref p, - value_map, - block_map, - }) = block_q.pop_front() - { - // Copy this block and its children - if loops.is_loop_header(b).is_some() { - treeify_loop( - b, - p, - function, - &cfg, - &loops, - &mut block_q, - value_map, - block_map, - )?; - } else { - treeify( - b, - p, - function, - &cfg, - &loops, - &mut block_q, - value_map, - block_map, - )?; - } - } - } - - // After treeification, the original subtree blocks cannot possibly be - // referenced by other blocks in the function, so remove all of them - detach_tree(b, function, &cfg); - - // Mark the control flow graph as modified - changed = true; - } - } - - // If we made any changes, we need to recompute all analyses - if !changed { - analyses.mark_all_preserved::(&function.id); - } - - Ok(()) - } -} - -#[allow(clippy::too_many_arguments)] -fn treeify( - b: BlockId, - p: &BlockPredecessor, - function: &mut hir::Function, - cfg: &ControlFlowGraph, - loops: &LoopAnalysis, - block_q: &mut VecDeque, - mut value_map: ScopedMap, - mut block_map: ScopedMap, -) -> anyhow::Result<()> { - // 1. Create a new block `b'`, without block arguments, - let b_prime = function.dfg.create_block_after(p.block); - block_map.insert(b, b_prime); - // 2. Initialize a lookup table of old value defs to new value defs, seed it by mapping the - // block arguments of `b` to the values passed from the predecessor - 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::MultiDest(ref jts) => { - for jt in jts.iter() { - if jt.destination == b { - value_map.extend( - function - .dfg - .block_args(b) - .iter() - .copied() - .zip(jt.args.iter().copied()), - ); - break; - } - } - } - BranchInfo::NotABranch => unreachable!(), - } - // 3. Update the predecessor instruction to reference the new block, remove block arguments. - update_predecessor(function, p, |dest, dest_args, pool| { - if *dest == b { - *dest = b_prime; - dest_args.clear(pool); - } - }); - // 4. Copy contents of `b` to `b'`, inserting defs in the lookup table, and mapping operands - // to their new "corrected" values - copy_instructions(b, b_prime, function, &mut value_map, &block_map); - // 5. Recursively copy all children of `b` to `b_prime` - copy_children( - b, b_prime, function, cfg, loops, block_q, value_map, block_map, - ) -} - -#[allow(clippy::too_many_arguments)] -fn treeify_loop( - b: BlockId, - p: &BlockPredecessor, - function: &mut hir::Function, - cfg: &ControlFlowGraph, - loops: &LoopAnalysis, - block_q: &mut VecDeque, - mut value_map: ScopedMap, - mut block_map: ScopedMap, -) -> anyhow::Result<()> { - // 1. Create new block, b', with a new set of block arguments matching the original, - // populate the value map with rewrites for the original block argument values - let b_prime = function.dfg.create_block_after(p.block); - block_map.insert(b, b_prime); - function.dfg.clone_block_params(b, b_prime); - for (src, dest) in function - .dfg - .block_params(b) - .iter() - .copied() - .zip(function.dfg.block_params(b_prime).iter().copied()) - { - value_map.insert(src, dest); - } - // 2. Update the predecessor instruction to reference the new block, leave block arguments unchanged - update_predecessor(function, p, |dest, _, _| { - if *dest == b { - *dest = b_prime; - } - }); - // 3. Copy contents of `b` to `b'`, inserting defs in the lookup table, and mapping operands - // to their new "corrected" values - copy_instructions(b, b_prime, function, &mut value_map, &block_map); - // 4. Recursively copy all children of `b` to `b_prime` - copy_children( - b, b_prime, function, cfg, loops, block_q, value_map, block_map, - ) -} - -/// Detach `root`, and all of it's reachable children, from the layout of `function` -/// -/// When called, it is assumed that `root` has been cloned to a new block, -/// along with all of it's reachable children, and its predecessor rewritten -/// to refer to the new block instead. As a result, `root` should no longer be -/// reachable in the CFG, along with its children, as they would have been cloned -/// as well. -/// -/// NOTE: This does not delete the block data attached to the function, only the -/// presence of the block in the layout of the function. -fn detach_tree(root: BlockId, function: &mut hir::Function, cfg: &ControlFlowGraph) { - let mut delete_q = VecDeque::::default(); - let mut visited = FxHashSet::::default(); - delete_q.push_back(root); - visited.insert(root); - while let Some(block) = delete_q.pop_front() { - function.dfg.detach_block(block); - for b in cfg.succ_iter(block) { - // Skip blocks we've already seen - if visited.insert(b) { - delete_q.push_back(b); - } - } - } -} - -#[allow(clippy::too_many_arguments)] -fn copy_children( - b: BlockId, - b_prime: BlockId, - function: &mut hir::Function, - cfg: &ControlFlowGraph, - loops: &LoopAnalysis, - block_q: &mut VecDeque, - value_map: ScopedMap, - block_map: ScopedMap, -) -> anyhow::Result<()> { - let pred = BlockPredecessor { - inst: function - .dfg - .last_inst(b_prime) - .expect("expected non-empty block"), - block: b_prime, - }; - let value_map = Rc::new(value_map); - let block_map = Rc::new(block_map); - for succ in cfg.succ_iter(b) { - // If we've already seen this successor, and it is a loop header, then - // we don't want to copy it, but we do want to replace the reference to - // this block with its copy - if let Some(succ_prime) = block_map.get(&succ) { - if loops.is_loop_header(succ).is_some() { - update_predecessor(function, &pred, |dest, _, _| { - if dest == &succ { - *dest = *succ_prime; - } - }); - continue; - } - } - - block_q.push_back(CopyBlock { - b: succ, - p: pred, - value_map: ScopedMap::new(Some(value_map.clone())), - block_map: ScopedMap::new(Some(block_map.clone())), - }); - } - - Ok(()) -} - -fn copy_instructions( - b: BlockId, - b_prime: BlockId, - function: &mut hir::Function, - value_map: &mut ScopedMap, - block_map: &ScopedMap, -) { - // Initialize the cursor at the first instruction in `b` - let mut next = { - let cursor = function.dfg.block(b).insts.front(); - cursor.get().map(|inst_data| inst_data as *const InstNode) - }; - - while let Some(ptr) = next.take() { - // Get the id of the instruction at the current cursor position, then advance the cursor - let src_inst = { - let mut cursor = unsafe { function.dfg.block(b).insts.cursor_from_ptr(ptr) }; - let id = cursor.get().unwrap().key; - cursor.move_next(); - next = cursor.get().map(|inst_data| inst_data as *const InstNode); - id - }; - - // Clone the source instruction data - let inst = function.dfg.clone_inst(src_inst); - - // We need to fix up the cloned instruction data - let data = &mut function.dfg.insts[inst]; - // First, we're going to be placing it in b', so make sure the instruction is aware of that - data.block = b_prime; - // 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, - .. - }) => { - if let Some(new_dest) = block_map.get(destination) { - *destination = *new_dest; - } - let args = 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; - } - } - } - 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), - .. - }) => { - 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; - } - let then_args = then_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; - } - let else_args = else_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; - } - } - } - other => { - for arg in other - .arguments_mut(&mut function.dfg.value_lists) - .iter_mut() - { - if let Some(arg_prime) = value_map.get(arg) { - *arg = *arg_prime; - } - } - } - } - // Finally, append the cloned instruction to the block layout - let node = unsafe { UnsafeRef::from_raw(data) }; - function.dfg.block_mut(b_prime).insts.push_back(node); - value_map.extend( - function - .dfg - .inst_results(src_inst) - .iter() - .copied() - .zip(function.dfg.inst_results(inst).iter().copied()), - ); - } -} - -struct CopyBlock { - b: BlockId, - p: BlockPredecessor, - value_map: ScopedMap, - block_map: ScopedMap, -} -impl CopyBlock { - fn new(b: BlockId, p: BlockPredecessor) -> Self { - Self { - b, - p, - value_map: Default::default(), - block_map: Default::default(), - } - } -} - -#[inline] -fn update_predecessor(function: &mut hir::Function, p: &BlockPredecessor, mut callback: F) -where - F: FnMut(&mut BlockId, &mut ValueList, &mut ValueListPool), -{ - match &mut function.dfg.insts[p.inst].data.item { - Instruction::Br(hir::Br { - ref mut destination, - ref mut args, - .. - }) => { - callback(destination, args, &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), - .. - }) => { - assert_ne!(then_dest, else_dest, "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); - } - Instruction::Switch(_) => { - panic!("expected switch instructions to have been simplified prior to treeification") - } - _ => unreachable!(), - } -} - -#[cfg(test)] -mod tests { - use miden_hir::{ - pass::{AnalysisManager, RewritePass}, - testing::{self, TestContext}, - ModuleBuilder, - }; - use pretty_assertions::{assert_eq, assert_ne}; - - use crate::Treeify; - - /// Run the treeify pass on the IR of the [testing::sum_matrix] function. - /// - /// This function corresponds forms a directed, cyclic graph; containing a loop - /// two levels deep, with control flow paths that join multiple predecessors. - /// It has no critical edges, as if we had already run the [SplitCriticalEdges] - /// pass, and doesn't contain any superfluous blocks: - /// - /// We expect this pass to identify that the exit block, `blk0` has multiple predecessors - /// and is not a loop header, and thus a candidate for treeification. We expect `blk0` - /// to be duplicated, so that each of it's predecessors, `entry` and `blk2` respectively, - /// have their own copies of the block. The terminators of those blocks should be - /// updated accordingly. Additionally, because the new versions of `blk0` have only - /// a single predecessor, the block arguments previously needed, should be removed - /// and the `ret` instruction should directly reference the return value originally - /// provided via `entry`/`blk2`. - #[test] - fn treeify_simple_test() { - let context = TestContext::default(); - - // Define the 'test' module - let mut builder = ModuleBuilder::new("test"); - let id = testing::sum_matrix(&mut builder, &context); - let mut module = builder.build(); - let mut function = module - .cursor_mut_at(id.function) - .remove() - .expect("undefined function"); - - let original = function.to_string(); - let mut analyses = AnalysisManager::default(); - let mut rewrite = Treeify; - rewrite - .apply(&mut function, &mut analyses, &context.session) - .expect("treeification failed"); - - let expected = "pub fn sum_matrix(*mut u32, u32, u32) -> u32 { -block0(v0: *mut u32, v1: u32, v2: u32): - v10 = const.u32 0 : u32; - v11 = ptrtoint v0 : u32; - v12 = neq v11, 0 : i1; - condbr v12, block2, block7; - -block7: - ret v10; - -block2: - v13 = const.u32 0 : u32; - v14 = const.u32 0 : u32; - v15 = mul.checked v2, 4 : u32; - br block3(v10, v13, v14); - -block3(v4: u32, v5: u32, v6: u32): - v16 = lt v5, v1 : i1; - v17 = mul.checked v5, v15 : u32; - condbr v16, block4(v4, v5, v6), block8; - -block8: - ret v4; - -block4(v7: u32, v8: u32, v9: u32): - v18 = lt v9, v2 : i1; - condbr v18, block5, block6; - -block5: - v19 = mul.checked v9, 4 : u32; - v20 = add.checked v17, v19 : u32; - v21 = add.checked v11, v20 : u32; - v22 = inttoptr v21 : *mut u32; - v23 = load v22 : u32; - v24 = add.checked v7, v23 : u32; - v25 = incr.wrapping v9 : u32; - br block4(v24, v8, v25); - -block6: - v26 = incr.wrapping v8 : u32; - v27 = const.u32 0 : u32; - br block3(v7, v26, v27); -} -"; - - let transformed = function.to_string(); - assert_ne!(transformed, original); - assert_eq!(transformed.as_str(), expected); - } -} diff --git a/hir-type/Cargo.toml b/hir-type/Cargo.toml deleted file mode 100644 index cb8805f05..000000000 --- a/hir-type/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "miden-hir-type" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -smallvec.workspace = true diff --git a/hir-type/src/layout.rs b/hir-type/src/layout.rs deleted file mode 100644 index a09f093d1..000000000 --- a/hir-type/src/layout.rs +++ /dev/null @@ -1,920 +0,0 @@ -use alloc::{alloc::Layout, boxed::Box, collections::VecDeque, vec::Vec}; -use core::cmp::{self, Ordering}; - -use smallvec::SmallVec; - -use super::*; - -const FELT_SIZE: usize = core::mem::size_of::(); -const WORD_SIZE: usize = core::mem::size_of::<[u64; 4]>(); - -impl Type { - /// Convert this type into a vector of types corresponding to how this type - /// will be represented in memory. - /// - /// The largest "part" size is 32 bits, so types that fit in 32 bits remain - /// unchanged. For types larger than 32 bits, they will be broken up into parts - /// that do fit in 32 bits, preserving accurate types to the extent possible. - /// For types smaller than 32 bits, they will be merged into packed structs no - /// larger than 32 bits, to preserve the type information, and make it possible - /// to reason about how to extract parts of the original type. - /// - /// For an example, a struct of type `{ *ptr, u8, u8 }` will be encoded on the - /// operand stack as `[*ptr, {u8, u8}]`, where the first value is the 32-bit pointer - /// field, and the remaining fields are encoded as a 16-bit struct in the second value. - pub fn to_raw_parts(self) -> Option> { - match self { - Type::Unknown => None, - ty => { - let mut parts = SmallVec::<[Type; 4]>::default(); - let (part, mut rest) = ty.split(4); - parts.push(part); - while let Some(ty) = rest.take() { - let (part, remaining) = ty.split(4); - parts.push(part); - rest = remaining; - } - Some(parts) - } - } - } - - /// Split this type into two parts: - /// - /// * The first part is no more than `n` bytes in size, and may contain the type itself if it fits - /// * The second part is None if the first part is smaller than or equal in size to the requested split size - /// * The second part is Some if there is data left in the original type after the split. This part will be - /// a type that attempts to preserve, to the extent possible, the original type structure, but will fall back - /// to an array of bytes if a larger type must be split down the middle somewhere. - pub fn split(self, n: usize) -> (Type, Option) { - if n == 0 { - return (self, None); - } - - let size_in_bytes = self.size_in_bytes(); - if n >= size_in_bytes { - return (self, None); - } - - // The type is larger than the split size - match self { - ty @ (Self::U256 - | Self::I128 - | Self::U128 - | Self::I64 - | Self::U64 - | Self::F64 - | Self::Felt - | Self::I32 - | Self::U32 - | Self::Ptr(_) - | Self::I16 - | Self::U16) => { - let len = ty.size_in_bytes(); - let remaining = len - n; - match (n, remaining) { - (0, _) | (_, 0) => unreachable!(), - (1, 1) => (Type::U8, Some(Type::U8)), - (1, remaining) => (Type::U8, Some(Type::Array(Box::new(Type::U8), remaining))), - (taken, 1) => (Type::Array(Box::new(Type::U8), taken), Some(Type::U8)), - (taken, remaining) => ( - Type::Array(Box::new(Type::U8), taken), - Some(Type::Array(Box::new(Type::U8), remaining)), - ), - } - } - Self::NativePtr(pointee, _) => { - let struct_ty = Type::Struct(StructType { - repr: TypeRepr::Default, - size: 12, - fields: Vec::from([ - StructField { - index: 0, - align: 4, - offset: 0, - ty: Type::Ptr(pointee), - }, - StructField { - index: 1, - align: 4, - offset: 4, - ty: Type::U8, - }, - StructField { - index: 2, - align: 4, - offset: 8, - ty: Type::U8, - }, - ]), - }); - struct_ty.split(n) - } - Self::Array(elem_ty, 1) => elem_ty.split(n), - Self::Array(elem_ty, array_len) => { - let elem_size = elem_ty.size_in_bytes(); - if n >= elem_size { - // The requested split consumes 1 or more elements.. - let take = n / elem_size; - let extra = n % elem_size; - if extra == 0 { - // The split is on an element boundary - let split = match take { - 1 => (*elem_ty).clone(), - _ => Self::Array(elem_ty.clone(), take), - }; - let rest = match array_len - take { - 0 => unreachable!(), - 1 => *elem_ty, - len => Self::Array(elem_ty, len), - }; - (split, Some(rest)) - } else { - // The element type must be split somewhere in order to get the input type down to the requested size - let (partial1, partial2) = (*elem_ty).clone().split(elem_size - extra); - match array_len - take { - 0 => unreachable!(), - 1 => { - let taken = Self::Array(elem_ty, take); - let split = Self::Struct(StructType::new_with_repr( - TypeRepr::packed(1), - [taken, partial1], - )); - (split, partial2) - } - remaining => { - let remaining_input = Self::Array(elem_ty.clone(), remaining); - let taken = Self::Array(elem_ty, take); - let split = Self::Struct(StructType::new_with_repr( - TypeRepr::packed(1), - [taken, partial1], - )); - let rest = Self::Struct(StructType::new_with_repr( - TypeRepr::packed(1), - [partial2.unwrap(), remaining_input], - )); - (split, Some(rest)) - } - } - } - } else { - // The requested split consumes less than one element - let (partial1, partial2) = (*elem_ty).clone().split(n); - let remaining_input = match array_len - 1 { - 0 => unreachable!(), - 1 => (*elem_ty).clone(), - len => Self::Array(elem_ty, len - 1), - }; - let rest = Self::Struct(StructType::new_with_repr( - TypeRepr::packed(1), - [partial2.unwrap(), remaining_input], - )); - (partial1, Some(rest)) - } - } - Self::Struct(StructType { - repr: TypeRepr::Transparent, - fields, - .. - }) => { - let underlying = fields - .into_iter() - .find(|f| !f.ty.is_zst()) - .expect("invalid type: expected non-zero sized field"); - underlying.ty.split(n) - } - Self::Struct(struct_ty) => { - let original_repr = struct_ty.repr; - let original_size = struct_ty.size; - let mut fields = VecDeque::from(struct_ty.fields); - let mut split = StructType { - repr: original_repr, - size: 0, - fields: Vec::new(), - }; - let mut remaining = StructType { - repr: TypeRepr::packed(1), - size: 0, - fields: Vec::new(), - }; - let mut needed: u32 = n.try_into().expect("invalid type split: number of bytes is larger than what is representable in memory"); - let mut current_offset = 0u32; - while let Some(mut field) = fields.pop_front() { - let padding = field.offset - current_offset; - // If the padding was exactly what was needed, add it to the `split` - // struct, and then place the remaining fields in a new struct - let original_offset = field.offset; - if padding == needed { - split.size += needed; - // Handle the edge case where padding is at the front of the struct - if split.fields.is_empty() { - split.fields.push(StructField { - index: 0, - align: 1, - offset: 0, - ty: Type::Array(Box::new(Type::U8), needed as usize), - }); - } - let mut prev_offset = original_offset; - let mut field_offset = 0; - field.index = 0; - field.offset = field_offset; - remaining.repr = TypeRepr::Default; - remaining.size = original_size - split.size; - remaining.fields.reserve(1 + fields.len()); - field_offset += field.ty.size_in_bytes() as u32; - remaining.fields.push(field); - for (index, mut field) in fields.into_iter().enumerate() { - field.index = (index + 1) as u8; - let align_offset = field.offset - prev_offset; - let field_size = field.ty.size_in_bytes() as u32; - prev_offset = field.offset + field_size; - field.offset = field_offset + align_offset; - field_offset += align_offset; - field_offset += field_size; - remaining.fields.push(field); - } - break; - } - - // If the padding is more than was needed, we fill out the rest of the - // request by padding the size of the `split` struct, and then adjust - // the remaining struct to account for the leftover padding. - if padding > needed { - // The struct size must match the requested split size - split.size += needed; - // Handle the edge case where padding is at the front of the struct - if split.fields.is_empty() { - split.fields.push(StructField { - index: 0, - align: 1, - offset: 0, - ty: Type::Array(Box::new(Type::U8), needed as usize), - }); - } - // What's left must account for what has been split off - let leftover_padding = u16::try_from(padding - needed).expect( - "invalid type: padding is larger than maximum allowed alignment", - ); - let effective_alignment = leftover_padding.prev_power_of_two(); - let align_offset = leftover_padding % effective_alignment; - let default_alignment = cmp::max( - fields.iter().map(|f| f.align).max().unwrap_or(1), - field.align, - ); - let repr = match default_alignment.cmp(&effective_alignment) { - Ordering::Equal => TypeRepr::Default, - Ordering::Greater => TypeRepr::packed(effective_alignment), - Ordering::Less => TypeRepr::align(effective_alignment), - }; - let mut prev_offset = original_offset; - let mut field_offset = align_offset as u32; - field.index = 0; - field.offset = field_offset; - remaining.repr = repr; - remaining.size = original_size - split.size; - remaining.fields.reserve(1 + fields.len()); - field_offset += field.ty.size_in_bytes() as u32; - remaining.fields.push(field); - for (index, mut field) in fields.into_iter().enumerate() { - field.index = (index + 1) as u8; - let align_offset = field.offset - prev_offset; - let field_size = field.ty.size_in_bytes() as u32; - prev_offset = field.offset + field_size; - field.offset = field_offset + align_offset; - field_offset += align_offset; - field_offset += field_size; - remaining.fields.push(field); - } - break; - } - - // The padding must be less than what was needed, so consume it, and - // then process the current field for the rest of the request - split.size += padding; - needed -= padding; - current_offset += padding; - let field_size = field.ty.size_in_bytes() as u32; - // If the field fully satisifies the remainder of the request, then - // finalize the `split` struct, and place remaining fields in a trailing - // struct with an appropriate repr - if field_size == needed { - split.size += field_size; - field.offset = current_offset; - split.fields.push(field); - - debug_assert!(!fields.is_empty(), "expected struct that is the exact size of the split request to have been handled elsewhere"); - - remaining.repr = original_repr; - remaining.size = original_size - split.size; - remaining.fields.reserve(fields.len()); - let mut prev_offset = current_offset + field_size; - let mut field_offset = 0; - for (index, mut field) in fields.into_iter().enumerate() { - field.index = index as u8; - let align_offset = field.offset - prev_offset; - let field_size = field.ty.size_in_bytes() as u32; - prev_offset = field.offset + field_size; - field.offset = field_offset + align_offset; - field_offset += align_offset; - field_offset += field_size; - remaining.fields.push(field); - } - break; - } - - // If the field is larger than what is needed, we have to split it - if field_size > needed { - split.size += needed; - - // Add the portion needed to `split` - let index = field.index; - let offset = current_offset; - let align = field.align; - let (partial1, partial2) = field.ty.split(needed as usize); - // The second half of the split will always be a type - let partial2 = partial2.unwrap(); - split.fields.push(StructField { - index, - offset, - align, - ty: partial1, - }); - - // Build a struct with the remaining fields and trailing partial field - let mut prev_offset = current_offset + needed; - let mut field_offset = needed + partial2.size_in_bytes() as u32; - remaining.size = original_size - split.size; - remaining.fields.reserve(1 + fields.len()); - remaining.fields.push(StructField { - index: 0, - offset: 1, - align: 1, - ty: partial2, - }); - for (index, mut field) in fields.into_iter().enumerate() { - field.index = (index + 1) as u8; - let align_offset = field.offset - prev_offset; - let field_size = field.ty.size_in_bytes() as u32; - prev_offset = field.offset + needed + field_size; - field.offset = field_offset + align_offset; - field_offset += align_offset; - field_offset += field_size; - remaining.fields.push(field); - } - break; - } - - // We need to process more fields for this request (i.e. field_size < needed) - needed -= field_size; - split.size += field_size; - field.offset = current_offset; - current_offset += field_size; - split.fields.push(field); - } - - let split = if split.fields.len() > 1 { - Type::Struct(split) - } else { - split.fields.pop().map(|f| f.ty).unwrap() - }; - match remaining.fields.len() { - 0 => (split, None), - 1 => (split, remaining.fields.pop().map(|f| f.ty)), - _ => (split, Some(remaining.into())), - } - } - // These types either have no size, or are 1 byte in size, so must have - // been handled above when checking if the size of the type is <= the - // requested split size - Self::Unknown | Self::Unit | Self::Never | Self::I1 | Self::U8 | Self::I8 => { - unreachable!() - } - } - } - - /// Returns the minimum alignment, in bytes, of this type - pub fn min_alignment(&self) -> usize { - match self { - // These types don't have a meaningful alignment, so choose byte-aligned - Self::Unknown | Self::Unit | Self::Never => 1, - // Felts must be naturally aligned to a 32-bit boundary (4 bytes) - Self::Felt => 4, - // 256-bit and 128-bit integers must be word-aligned - Self::U256 | Self::I128 | Self::U128 => 16, - // 64-bit integers and floats must be element-aligned - Self::I64 | Self::U64 | Self::F64 => 4, - // 32-bit integers and pointers must be element-aligned - Self::I32 | Self::U32 | Self::Ptr(_) | Self::NativePtr(_, _) => 4, - // 16-bit integers can be naturally aligned - Self::I16 | Self::U16 => 2, - // 8-bit integers and booleans can be naturally aligned - Self::I8 | Self::U8 | Self::I1 => 1, - // Structs use the minimum alignment of their first field, or 1 if a zero-sized type - Self::Struct(ref struct_ty) => struct_ty.min_alignment(), - // Arrays use the minimum alignment of their element type - Self::Array(ref element_ty, _) => element_ty.min_alignment(), - } - } - - /// Returns the size in bits of this type, without alignment padding. - pub fn size_in_bits(&self) -> usize { - match self { - // These types have no representation in memory - Self::Unknown | Self::Unit | Self::Never => 0, - // Booleans are represented as i1 - Self::I1 => 1, - // Integers are naturally sized - Self::I8 | Self::U8 => 8, - Self::I16 | Self::U16 => 16, - // Field elements have a range that is almost 64 bits, but because - // our byte-addressable memory model only sees each element as a 32-bit - // chunk, we treat field elements in this model as 32-bit values. This - // has no effect on their available range, just how much memory they are - // assumed to require for storage. - Self::I32 | Self::U32 | Self::Felt => 32, - Self::I64 | Self::U64 | Self::F64 => 64, - Self::I128 | Self::U128 => 128, - Self::U256 => 256, - // Raw pointers are 32-bits, the same size as the native integer width, u32 - Self::Ptr(_) => 32, - // Native pointers are essentially a tuple/struct, composed of three 32-bit parts - Self::NativePtr(_, _) => 96, - // Packed structs have no alignment padding between fields - Self::Struct(ref struct_ty) => struct_ty.size as usize * 8, - // Zero-sized arrays have no size in memory - Self::Array(_, 0) => 0, - // An array of one element is the same as just the element - Self::Array(ref element_ty, 1) => element_ty.size_in_bits(), - // All other arrays require alignment padding between elements - Self::Array(ref element_ty, n) => { - let min_align = element_ty.min_alignment() * 8; - let element_size = element_ty.size_in_bits(); - let padded_element_size = element_size.align_up(min_align); - element_size + (padded_element_size * (n - 1)) - } - } - } - - /// Returns the minimum number of bytes required to store a value of this type - pub fn size_in_bytes(&self) -> usize { - let bits = self.size_in_bits(); - (bits / 8) + (bits % 8 > 0) as usize - } - - /// Same as `size_in_bytes`, but with sufficient padding to guarantee alignment of the value. - pub fn aligned_size_in_bytes(&self) -> usize { - let align = self.min_alignment(); - let size = self.size_in_bytes(); - // Zero-sized types have no alignment - if size == 0 { - return 0; - } - - // Assuming that a pointer is allocated with the worst possible alignment, - // i.e. it is not aligned on a power-of-two boundary, we can ensure that there - // is enough space to align the pointer to the required minimum alignment and - // still fit it in the allocated block of memory without overflowing its bounds, - // by adding `align` to size. - // - // We panic if padding the size overflows `usize`. - // - // So let's say we have a type with a min alignment of 16, and size of 24. If - // we add 16 to 24, we get 40. We then allocate a block of memory of 40 bytes, - // the pointer of which happens to be at address 0x01. If we align that pointer - // to 0x10 (the next closest aligned address within the block we allocated), - // that consumes 15 bytes of the 40 we have, leaving us with 25 bytes to hold - // our 24 byte value. - size.checked_add(align) - .expect("type cannot meet its minimum alignment requirement due to its size") - } - - /// Returns the size in field elements of this type - pub fn size_in_felts(&self) -> usize { - let bytes = self.size_in_bytes(); - let trailing = bytes % FELT_SIZE; - (bytes / FELT_SIZE) + ((trailing > 0) as usize) - } - - /// Returns the size in words of this type - pub fn size_in_words(&self) -> usize { - let bytes = self.size_in_bytes(); - let trailing = bytes % WORD_SIZE; - (bytes / WORD_SIZE) + ((trailing > 0) as usize) - } - - /// Returns the layout of this type in memory - pub fn layout(&self) -> Layout { - Layout::from_size_align(self.size_in_bytes(), self.min_alignment()) - .expect("invalid layout: the size, when padded for alignment, overflows isize") - } - - /// Returns true if this type can be loaded on to the operand stack - /// - /// The rule for "loadability" is a bit arbitrary, but the purpose is to - /// force users of the IR to either pass large values by reference, or calculate - /// the addresses of the individual fields needed from a large structure or array, - /// and issue loads/stores against those instead. - /// - /// In effect, we reject loads of values that are larger than a single word, as that - /// is the largest value which can be worked with on the operand stack of the Miden VM. - pub fn is_loadable(&self) -> bool { - self.size_in_words() <= WORD_SIZE - } -} - -/// This trait represents an alignable primitive integer value representing an address -pub trait Alignable { - /// This function computes the offset, in bytes, needed to align `self` upwards so that - /// it is aligned to `align` bytes. - /// - /// The following must be true, or this function will panic: - /// - /// * `align` is non-zero - /// * `align` is a power of two - fn align_offset(self, align: Self) -> Self; - /// This function aligns `self` to the specified alignment (in bytes), aligning upwards. - /// - /// The following must be true, or this function will panic: - /// - /// * `align` is non-zero - /// * `align` is a power of two - /// * `self` + `align` must be less than `Self::MAX` - fn align_up(self, align: Self) -> Self; - - /// Compute the smallest value greater than or equal to `self` that is a multiple of `m` - /// - /// The following must be true, or this function will panic: - /// - /// * `m` must be non-zero - /// * `self` + `m` must be less than `Self::MAX` - /// - /// TODO: Replace this with the standard library `next_multiple_of` when 1.73 drops. - fn next_multiple_of(self, m: Self) -> Self; - - /// Compute the nearest power of two less than or equal to `self` - fn prev_power_of_two(self) -> Self; -} - -macro_rules! alignable { - ($($ty:ty),+) => { - $( - alignable_impl!($ty); - )* - }; -} - -macro_rules! alignable_impl { - ($ty:ty) => { - #[allow(unstable_name_collisions)] - impl Alignable for $ty { - #[inline] - fn align_offset(self, align: Self) -> Self { - assert!(align.is_power_of_two()); - self.next_multiple_of(align) - self - } - - #[inline] - fn align_up(self, align: Self) -> Self { - assert!(self.saturating_add(align) < Self::MAX); - self.next_multiple_of(align) - } - - #[inline] - fn next_multiple_of(self, m: Self) -> Self { - // The offset in from the last multiple of `m` to reach `n` - let offset = self % m; - // If `n` is a multiple of `m`, this is 0, else 1 - let is_not_multiple = (offset > 0) as $ty; - // Apply offset to `n` to reach the next nearest multiple of `m` - // - // If `n` is already a multiple of `m`, the offset is 0 - self + ((m - offset) * is_not_multiple) - } - - #[inline] - fn prev_power_of_two(self) -> Self { - if self.is_power_of_two() { - self - } else { - cmp::max(self.next_power_of_two() / 2, 1) - } - } - } - }; -} - -alignable!(u8, u16, u32, u64, usize); - -#[cfg(test)] -#[allow(unstable_name_collisions)] -mod tests { - use crate::*; - use smallvec::smallvec; - - #[test] - fn struct_type_test() { - let ptr_ty = Type::Ptr(Box::new(Type::U32)); - // A struct with default alignment and padding between fields - let struct_ty = StructType::new([ptr_ty.clone(), Type::U8, Type::I32]); - assert_eq!(struct_ty.min_alignment(), ptr_ty.min_alignment()); - assert_eq!(struct_ty.size(), 12); - assert_eq!( - struct_ty.get(0), - &StructField { - index: 0, - align: 4, - offset: 0, - ty: ptr_ty.clone() - } - ); - assert_eq!( - struct_ty.get(1), - &StructField { - index: 1, - align: 1, - offset: 4, - ty: Type::U8 - } - ); - assert_eq!( - struct_ty.get(2), - &StructField { - index: 2, - align: 4, - offset: 8, - ty: Type::I32 - } - ); - - // A struct with no alignment requirement, and no alignment padding between fields - let struct_ty = - StructType::new_with_repr(TypeRepr::packed(1), [ptr_ty.clone(), Type::U8, Type::I32]); - assert_eq!(struct_ty.min_alignment(), 1); - assert_eq!(struct_ty.size(), 9); - assert_eq!( - struct_ty.get(0), - &StructField { - index: 0, - align: 1, - offset: 0, - ty: ptr_ty.clone() - } - ); - assert_eq!( - struct_ty.get(1), - &StructField { - index: 1, - align: 1, - offset: 4, - ty: Type::U8 - } - ); - assert_eq!( - struct_ty.get(2), - &StructField { - index: 2, - align: 1, - offset: 5, - ty: Type::I32 - } - ); - - // A struct with larger-than-default alignment, but default alignment for the fields - let struct_ty = - StructType::new_with_repr(TypeRepr::align(8), [ptr_ty.clone(), Type::U8, Type::I32]); - assert_eq!(struct_ty.min_alignment(), 8); - assert_eq!(struct_ty.size(), 16); - assert_eq!( - struct_ty.get(0), - &StructField { - index: 0, - align: 4, - offset: 0, - ty: ptr_ty.clone() - } - ); - assert_eq!( - struct_ty.get(1), - &StructField { - index: 1, - align: 1, - offset: 4, - ty: Type::U8 - } - ); - assert_eq!( - struct_ty.get(2), - &StructField { - index: 2, - align: 4, - offset: 8, - ty: Type::I32 - } - ); - } - - #[test] - fn type_to_raw_parts_test() { - let ty = Type::Array(Box::new(Type::U8), 5); - assert_eq!( - ty.to_raw_parts(), - Some(smallvec![Type::Array(Box::new(Type::U8), 4), Type::U8,]) - ); - - let ty = Type::Array(Box::new(Type::I16), 3); - assert_eq!( - ty.to_raw_parts(), - Some(smallvec![Type::Array(Box::new(Type::I16), 2), Type::I16,]) - ); - - let native_ptr_ty = Type::NativePtr(Box::new(Type::U32), AddressSpace::Root); - let ptr_ty = Type::Ptr(Box::new(Type::U32)); - let ty = Type::Array(Box::new(native_ptr_ty), 2); - assert_eq!( - ty.to_raw_parts(), - Some(smallvec![ - ptr_ty.clone(), - Type::U8, - Type::U8, - ptr_ty.clone(), - Type::U8, - Type::U8 - ]) - ); - - // Default struct - let ty = Type::Struct(StructType::new([ptr_ty.clone(), Type::U8, Type::I32])); - assert_eq!( - ty.to_raw_parts(), - Some(smallvec![ptr_ty.clone(), Type::U8, Type::I32,]) - ); - - // Packed struct - let ty = Type::Struct(StructType::new_with_repr( - TypeRepr::packed(1), - [ptr_ty.clone(), Type::U8, Type::I32], - )); - let partial_ty = Type::Struct(StructType::new_with_repr( - TypeRepr::packed(1), - [Type::U8, Type::Array(Box::new(Type::U8), 3)], - )); - assert_eq!( - ty.to_raw_parts(), - Some(smallvec![ptr_ty.clone(), partial_ty, Type::U8]) - ); - } - - #[test] - fn alignable_next_multiple_of() { - let addr = 0u32; - assert_eq!(addr.next_multiple_of(1), 0); - assert_eq!(addr.next_multiple_of(2), 0); - assert_eq!(addr.next_multiple_of(4), 0); - assert_eq!(addr.next_multiple_of(8), 0); - assert_eq!(addr.next_multiple_of(16), 0); - assert_eq!(addr.next_multiple_of(32), 0); - - let addr = 1u32; - assert_eq!(addr.next_multiple_of(1), 1); - assert_eq!(addr.next_multiple_of(2), 2); - assert_eq!(addr.next_multiple_of(4), 4); - assert_eq!(addr.next_multiple_of(8), 8); - assert_eq!(addr.next_multiple_of(16), 16); - assert_eq!(addr.next_multiple_of(32), 32); - - let addr = 2u32; - assert_eq!(addr.next_multiple_of(1), 2); - assert_eq!(addr.next_multiple_of(2), 2); - assert_eq!(addr.next_multiple_of(4), 4); - assert_eq!(addr.next_multiple_of(8), 8); - assert_eq!(addr.next_multiple_of(16), 16); - assert_eq!(addr.next_multiple_of(32), 32); - - let addr = 3u32; - assert_eq!(addr.next_multiple_of(1), 3); - assert_eq!(addr.next_multiple_of(2), 4); - assert_eq!(addr.next_multiple_of(4), 4); - assert_eq!(addr.next_multiple_of(8), 8); - assert_eq!(addr.next_multiple_of(16), 16); - assert_eq!(addr.next_multiple_of(32), 32); - - let addr = 127u32; - assert_eq!(addr.next_multiple_of(1), 127); - assert_eq!(addr.next_multiple_of(2), 128); - assert_eq!(addr.next_multiple_of(4), 128); - assert_eq!(addr.next_multiple_of(8), 128); - assert_eq!(addr.next_multiple_of(16), 128); - assert_eq!(addr.next_multiple_of(32), 128); - - let addr = 130u32; - assert_eq!(addr.next_multiple_of(1), 130); - assert_eq!(addr.next_multiple_of(2), 130); - assert_eq!(addr.next_multiple_of(4), 132); - assert_eq!(addr.next_multiple_of(8), 136); - assert_eq!(addr.next_multiple_of(16), 144); - assert_eq!(addr.next_multiple_of(32), 160); - } - - #[test] - fn alignable_align_offset_test() { - let addr = 0u32; - assert_eq!(addr.align_offset(1), 0); - assert_eq!(addr.align_offset(2), 0); - assert_eq!(addr.align_offset(4), 0); - assert_eq!(addr.align_offset(8), 0); - assert_eq!(addr.align_offset(16), 0); - assert_eq!(addr.align_offset(32), 0); - - let addr = 1u32; - assert_eq!(addr.align_offset(1), 0); - assert_eq!(addr.align_offset(2), 1); - assert_eq!(addr.align_offset(4), 3); - assert_eq!(addr.align_offset(8), 7); - assert_eq!(addr.align_offset(16), 15); - assert_eq!(addr.align_offset(32), 31); - - let addr = 2u32; - assert_eq!(addr.align_offset(1), 0); - assert_eq!(addr.align_offset(2), 0); - assert_eq!(addr.align_offset(4), 2); - assert_eq!(addr.align_offset(8), 6); - assert_eq!(addr.align_offset(16), 14); - assert_eq!(addr.align_offset(32), 30); - - let addr = 3u32; - assert_eq!(addr.align_offset(1), 0); - assert_eq!(addr.align_offset(2), 1); - assert_eq!(addr.align_offset(4), 1); - assert_eq!(addr.align_offset(8), 5); - assert_eq!(addr.align_offset(16), 13); - assert_eq!(addr.align_offset(32), 29); - - let addr = 127u32; - assert_eq!(addr.align_offset(1), 0); - assert_eq!(addr.align_offset(2), 1); - assert_eq!(addr.align_offset(4), 1); - assert_eq!(addr.align_offset(8), 1); - assert_eq!(addr.align_offset(16), 1); - assert_eq!(addr.align_offset(32), 1); - - let addr = 130u32; - assert_eq!(addr.align_offset(1), 0); - assert_eq!(addr.align_offset(2), 0); - assert_eq!(addr.align_offset(4), 2); - assert_eq!(addr.align_offset(8), 6); - assert_eq!(addr.align_offset(16), 14); - assert_eq!(addr.align_offset(32), 30); - } - - #[test] - fn alignable_align_up_test() { - let addr = 0u32; - assert_eq!(addr.align_up(1), 0); - assert_eq!(addr.align_up(2), 0); - assert_eq!(addr.align_up(4), 0); - assert_eq!(addr.align_up(8), 0); - assert_eq!(addr.align_up(16), 0); - assert_eq!(addr.align_up(32), 0); - - let addr = 1u32; - assert_eq!(addr.align_up(1), 1); - assert_eq!(addr.align_up(2), 2); - assert_eq!(addr.align_up(4), 4); - assert_eq!(addr.align_up(8), 8); - assert_eq!(addr.align_up(16), 16); - assert_eq!(addr.align_up(32), 32); - - let addr = 2u32; - assert_eq!(addr.align_up(1), 2); - assert_eq!(addr.align_up(2), 2); - assert_eq!(addr.align_up(4), 4); - assert_eq!(addr.align_up(8), 8); - assert_eq!(addr.align_up(16), 16); - assert_eq!(addr.align_up(32), 32); - - let addr = 3u32; - assert_eq!(addr.align_up(1), 3); - assert_eq!(addr.align_up(2), 4); - assert_eq!(addr.align_up(4), 4); - assert_eq!(addr.align_up(8), 8); - assert_eq!(addr.align_up(16), 16); - assert_eq!(addr.align_up(32), 32); - - let addr = 127u32; - assert_eq!(addr.align_up(1), 127); - assert_eq!(addr.align_up(2), 128); - assert_eq!(addr.align_up(4), 128); - assert_eq!(addr.align_up(8), 128); - assert_eq!(addr.align_up(16), 128); - assert_eq!(addr.align_up(32), 128); - - let addr = 130u32; - assert_eq!(addr.align_up(1), 130); - assert_eq!(addr.align_up(2), 130); - assert_eq!(addr.align_up(4), 132); - assert_eq!(addr.align_up(8), 136); - assert_eq!(addr.align_up(16), 144); - assert_eq!(addr.align_up(32), 160); - } -} diff --git a/hir-type/src/lib.rs b/hir-type/src/lib.rs deleted file mode 100644 index 512ed9315..000000000 --- a/hir-type/src/lib.rs +++ /dev/null @@ -1,711 +0,0 @@ -#![no_std] - -extern crate alloc; - -mod layout; - -pub use self::layout::Alignable; - -use alloc::{boxed::Box, vec::Vec}; -use core::{fmt, num::NonZeroU16, str::FromStr}; - -/// Represents the type of a value -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Type { - /// This indicates a failure to type a value, or a value which is untypable - Unknown, - /// This type is used to indicate the absence of a value, such as a function which returns - /// nothing - Unit, - /// This type is the bottom type, and represents divergence, akin to Rust's Never/! type - Never, - I1, - I8, - U8, - I16, - U16, - I32, - U32, - I64, - U64, - I128, - U128, - U256, - F64, - /// Field element - Felt, - /// A pointer to a value in the default byte-addressable address space used by the IR. - /// - /// Pointers of this type will be translated to an appropriate address space during code generation. - Ptr(Box), - /// A pointer to a valude in Miden's native word-addressable address space. - /// - /// In the type system, we represent the type of the pointee, as well as an address space identifier. - /// - /// This pointer type is represented on Miden's operand stack as a u64 value, consisting of - /// two 32-bit elements, the most-significant bits being on top of the stack: - /// - /// 1. Metadata for the pointer (in the upper 32-bit limb): - /// * The least-significant 2 bits represent a zero-based element index (range is 0-3) - /// * The next most significant 4 bits represent a zero-based byte index (range is 0-31) - /// * The remaining 26 bits represent an address space identifier - /// 2. The lower 32-bit limb contains the word-aligned address, which forms the base address of the pointer. - /// - /// Dereferencing a pointer of this type involves popping the pointer metadata, and determining what type - /// of load to issue based on the size of the value being loaded, and where the start of the data is - /// according to the metadata. Then the word-aligned address is popped and the value is loaded. - /// - /// If the load is naturally aligned, i.e. the element index and byte offset are zero, and the size is exactly - /// one element or word; then a mem_load or mem_loadw are issued and no further action is required. If the load - /// is not naturally aligned, then either one or two words will be loaded, depending on the type being loaded, - /// unused elements will be dropped, and if the byte offset is non-zero, the data will be shifted bitwise into - /// alignment on an element boundary. - NativePtr(Box, AddressSpace), - /// A compound type of fixed shape and size - Struct(StructType), - /// A vector of fixed size - Array(Box, usize), -} -impl Type { - /// Returns true if this type is a zero-sized type, which includes: - /// - /// * Types with no size, e.g. `Type::Unit` - /// * Zero-sized arrays - /// * Arrays with a zero-sized element type - /// * Structs composed of nothing but zero-sized fields - pub fn is_zst(&self) -> bool { - match self { - Self::Unknown => false, - Self::Never | Self::Unit => true, - Self::Array(_, 0) => true, - Self::Array(ref elem_ty, _) => elem_ty.is_zst(), - Self::Struct(ref struct_ty) => struct_ty.fields.iter().all(|f| f.ty.is_zst()), - Self::I1 - | Self::I8 - | Self::U8 - | Self::I16 - | Self::U16 - | Self::I32 - | Self::U32 - | Self::I64 - | Self::U64 - | Self::I128 - | Self::U128 - | Self::U256 - | Self::F64 - | Self::Felt - | Self::Ptr(_) - | Self::NativePtr(_, _) => false, - } - } - - pub fn is_numeric(&self) -> bool { - matches!( - self, - Self::I1 - | Self::I8 - | Self::U8 - | Self::I16 - | Self::U16 - | Self::I32 - | Self::U32 - | Self::I64 - | Self::U64 - | Self::I128 - | Self::U128 - | Self::U256 - | Self::F64 - | Self::Felt - ) - } - - pub fn is_integer(&self) -> bool { - matches!( - self, - Self::I1 - | Self::I8 - | Self::U8 - | Self::I16 - | Self::U16 - | Self::I32 - | Self::U32 - | Self::I64 - | Self::U64 - | Self::I128 - | Self::U128 - | Self::U256 - | Self::Felt - ) - } - - pub fn is_signed_integer(&self) -> bool { - matches!( - self, - Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 - ) - } - - pub fn is_unsigned_integer(&self) -> bool { - matches!( - self, - Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::U128 - ) - } - - /// Get this type as its unsigned integral twin, e.g. i32 becomes u32. - /// - /// This function will panic if the type is not an integer type, or has no unsigned representation - pub fn as_unsigned(&self) -> Type { - match self { - Self::I8 | Self::U8 => Self::U8, - Self::I16 | Self::U16 => Self::U16, - Self::I32 | Self::U32 => Self::U32, - Self::I64 | Self::U64 => Self::U64, - Self::Felt => Self::Felt, - Self::I128 => panic!( - "invalid conversion to unsigned integer type: i128 has no unsigned equivalent" - ), - ty => panic!("invalid conversion to unsigned integer type: {ty} is not an integer"), - } - } - - /// Get this type as its signed integral twin, e.g. u32 becomes i32. - /// - /// This function will panic if the type is not an integer type, or has no signed representation - pub fn as_signed(&self) -> Type { - match self { - Self::I8 | Self::U8 => Self::I8, - Self::I16 | Self::U16 => Self::I16, - Self::I32 | Self::U32 => Self::I32, - Self::I64 | Self::U64 => Self::I64, - Self::I128 => Self::I128, - Self::Felt => { - panic!("invalid conversion to signed integer type: felt has no signed equivalent") - } - ty => panic!("invalid conversion to signed integer type: {ty} is not an integer"), - } - } - - #[inline] - pub fn is_float(&self) -> bool { - matches!(self, Self::F64) - } - - #[inline] - pub fn is_felt(&self) -> bool { - matches!(self, Self::Felt) - } - - #[inline] - pub fn is_pointer(&self) -> bool { - matches!(self, Self::Ptr(_) | Self::NativePtr(_, _)) - } - - #[inline] - pub fn is_struct(&self) -> bool { - matches!(self, Self::Struct(_)) - } - - #[inline] - pub fn is_array(&self) -> bool { - matches!(self, Self::Array(_, _)) - } - - /// Returns true if `self` and `other` are compatible operand types for a binary operator, e.g. `add` - /// - /// In short, the rules are as follows: - /// - /// * The operand order is assumed to be `self other`, i.e. `op` is being applied - /// to `self` using `other`. The left-hand operand is used as the "controlling" type - /// for the operator, i.e. it determines what instruction will be used to perform the - /// operation. - /// * The operand types must be numeric, or support being manipulated numerically - /// * If the controlling type is unsigned, it is never compatible with signed types, because Miden - /// instructions for unsigned types use a simple unsigned binary encoding, thus they will not handle - /// signed operands using two's complement correctly. - /// * If the controlling type is signed, it is compatible with both signed and unsigned types, as long - /// as the values fit in the range of the controlling type, e.g. adding a `u16` to an `i32` is fine, - /// but adding a `u32` to an `i32` is not. - /// * Pointer types are permitted to be the controlling type, and since they are represented using u32, - /// they have the same compatibility set as u32 does. In all other cases, pointer types are treated - /// the same as any other non-numeric type. - /// * Non-numeric types are always incompatible, since no operators support these types - pub fn is_compatible_operand(&self, other: &Type) -> bool { - match (self, other) { - (Type::I1, Type::I1) => true, - (Type::I8, Type::I8) => true, - (Type::U8, Type::U8) => true, - (Type::I16, Type::I8 | Type::U8 | Type::I16) => true, - (Type::U16, Type::U8 | Type::U16) => true, - (Type::I32, Type::I8 | Type::U8 | Type::I16 | Type::U16 | Type::I32) => true, - (Type::U32, Type::U8 | Type::U16 | Type::U32) => true, - ( - Type::Felt, - Type::I8 | Type::U8 | Type::I16 | Type::U16 | Type::I32 | Type::U32 | Type::Felt, - ) => true, - ( - Type::I64, - Type::I8 - | Type::U8 - | Type::I16 - | Type::U16 - | Type::I32 - | Type::U32 - | Type::Felt - | Type::I64, - ) => true, - (Type::U64, Type::U8 | Type::U16 | Type::U32 | Type::U64) => true, - ( - Type::I128, - Type::I8 - | Type::U8 - | Type::I16 - | Type::U16 - | Type::I32 - | Type::U32 - | Type::Felt - | Type::I64 - | Type::U64 - | Type::I128, - ) => true, - (Type::U128, Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128) => true, - (Type::U256, rty) => rty.is_integer(), - (Type::F64, Type::F64) => true, - (Type::Ptr(_) | Type::NativePtr(_, _), Type::U8 | Type::U16 | Type::U32) => true, - _ => false, - } - } - - #[inline] - pub fn pointee(&self) -> Option<&Type> { - use core::ops::Deref; - match self { - Self::Ptr(ty) | Self::NativePtr(ty, _) => Some(ty.deref()), - _ => None, - } - } -} -impl From for Type { - #[inline] - fn from(ty: StructType) -> Type { - Type::Struct(ty) - } -} -impl fmt::Display for Type { - /// Print this type for display using the provided module context - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use core::fmt::Write; - match self { - Self::Unknown => f.write_str("?"), - Self::Unit => f.write_str("()"), - Self::Never => f.write_char('!'), - Self::I1 => f.write_str("i1"), - Self::I8 => f.write_str("i8"), - Self::U8 => f.write_str("u8"), - Self::I16 => f.write_str("i16"), - Self::U16 => f.write_str("u16"), - Self::I32 => f.write_str("i32"), - Self::U32 => f.write_str("u32"), - Self::I64 => f.write_str("i64"), - Self::U64 => f.write_str("u64"), - Self::I128 => f.write_str("i128"), - Self::U128 => f.write_str("u128"), - Self::U256 => f.write_str("u256"), - Self::F64 => f.write_str("f64"), - Self::Felt => f.write_str("felt"), - Self::Ptr(inner) => write!(f, "*mut {}", &inner), - Self::NativePtr(inner, addrspace) => { - write!(f, "*mut(addrspace {}) {}", inner, addrspace) - } - Self::Struct(sty) => write!(f, "{sty}"), - Self::Array(element_ty, arity) => write!(f, "[{}; {}]", &element_ty, arity), - } - } -} - -/// This represents metadata about how a structured type will be represented in memory -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum TypeRepr { - /// This corresponds to the C ABI representation for a given type - #[default] - Default, - /// This modifies the default representation, by raising the minimum alignment. - /// - /// The alignment must be a power of two, e.g. 32, and values from 1 to 2^16 are allowed. - /// - /// The alignment must be greater than the default minimum alignment of the type - /// or this representation has no effect. - Align(NonZeroU16), - /// This modifies the default representation, by lowering the minimum alignment of - /// a type, and in the case of structs, changes the alignments of the fields to be - /// the smaller of the specified alignment and the default alignment. This has the - /// effect of changing the layout of a struct. - /// - /// Notably, `Packed(1)` will result in a struct that has no alignment requirement, - /// and no padding between fields. - /// - /// The alignment must be a power of two, e.g. 32, and values from 1 to 2^16 are allowed. - /// - /// The alignment must be smaller than the default alignment, or this representation - /// has no effect. - Packed(NonZeroU16), - /// This may only be used on structs with no more than one non-zero sized field, and - /// indicates that the representation of that field should be used for the struct. - Transparent, -} -impl TypeRepr { - #[inline] - pub fn packed(align: u16) -> Self { - Self::Packed( - NonZeroU16::new(align).expect("invalid alignment: expected value in range 1..=65535"), - ) - } - - #[inline] - pub fn align(align: u16) -> Self { - Self::Align( - NonZeroU16::new(align).expect("invalid alignment: expected value in range 1..=65535"), - ) - } - - /// Return true if this type representation is transparent - pub fn is_transparent(&self) -> bool { - matches!(self, Self::Transparent) - } - - /// Return true if this type representation is packed - pub fn is_packed(&self) -> bool { - matches!(self, Self::Packed(_)) - } - - /// Get the custom alignment given for this type representation, if applicable - pub fn min_alignment(&self) -> Option { - match self { - Self::Packed(align) | Self::Align(align) => Some(align.get() as usize), - _ => None, - } - } -} - -/// This represents metadata about a field of a [StructType] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StructField { - /// The index of this field in the final layout - pub index: u8, - /// The specified alignment for this field - pub align: u16, - /// The offset of this field relative to the previous field, or from the base of the struct - pub offset: u32, - /// The type of this field - pub ty: Type, -} -impl fmt::Display for StructField { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", &self.ty) - } -} - -/// This represents a structured aggregate type -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StructType { - /// The representation to use for this type - pub(crate) repr: TypeRepr, - /// The computed size of this struct - pub(crate) size: u32, - /// The fields of this struct, in the original order specified - /// - /// The actual order of fields in the final layout is determined by the index - /// associated with each field, not the index in this vector, although for `repr(C)` - /// structs they will be the same - pub(crate) fields: Vec, -} -impl StructType { - /// Create a new struct with default representation, i.e. a struct with representation of `TypeRepr::Packed(1)`. - #[inline] - pub fn new>(fields: I) -> Self { - Self::new_with_repr(TypeRepr::Default, fields) - } - - /// Create a new struct with the given representation. - /// - /// This function will panic if the rules of the given representation are violated. - pub fn new_with_repr>(repr: TypeRepr, fields: I) -> Self { - let tys = fields.into_iter().collect::>(); - let mut fields = Vec::with_capacity(tys.len()); - let size = match repr { - TypeRepr::Transparent => { - let mut offset = 0u32; - for (index, ty) in tys.into_iter().enumerate() { - let index: u8 = index - .try_into() - .expect("invalid struct: expected no more than 255 fields"); - let field_size: u32 = ty - .size_in_bytes() - .try_into() - .expect("invalid type: size is larger than 2^32 bytes"); - if field_size == 0 { - fields.push(StructField { - index, - align: 1, - offset, - ty, - }); - } else { - let align = ty.min_alignment().try_into().expect("invalid struct field alignment: expected power of two between 1 and 2^16"); - assert_eq!(offset, 0, "invalid transparent representation for struct: repr(transparent) is only valid for structs with a single non-zero sized field"); - fields.push(StructField { - index, - align, - offset, - ty, - }); - offset += field_size; - } - } - offset - } - repr => { - let mut offset = 0u32; - let default_align: u16 = tys - .iter() - .map(|t| t.min_alignment()) - .max() - .unwrap_or(1) - .try_into() - .expect( - "invalid struct field alignment: expected power of two between 1 and 2^16", - ); - let align = match repr { - TypeRepr::Align(align) => core::cmp::max(align.get(), default_align), - TypeRepr::Packed(align) => core::cmp::min(align.get(), default_align), - TypeRepr::Transparent | TypeRepr::Default => default_align, - }; - - for (index, ty) in tys.into_iter().enumerate() { - let index: u8 = index - .try_into() - .expect("invalid struct: expected no more than 255 fields"); - let field_size: u32 = ty - .size_in_bytes() - .try_into() - .expect("invalid type: size is larger than 2^32 bytes"); - let default_align: u16 = ty.min_alignment().try_into().expect( - "invalid struct field alignment: expected power of two between 1 and 2^16", - ); - let align: u16 = match repr { - TypeRepr::Packed(align) => core::cmp::min(align.get(), default_align), - _ => default_align, - }; - offset += offset.align_offset(align as u32); - fields.push(StructField { - index, - align, - offset, - ty, - }); - offset += field_size; - } - offset.align_up(align as u32) - } - }; - Self { repr, size, fields } - } - - /// Get the [TypeRepr] for this struct - #[inline] - pub const fn repr(&self) -> TypeRepr { - self.repr - } - - /// Get the minimum alignment for this struct - pub fn min_alignment(&self) -> usize { - self.repr.min_alignment().unwrap_or_else(|| { - self.fields - .iter() - .map(|f| f.align as usize) - .max() - .unwrap_or(1) - }) - } - - /// Get the total size in bytes required to hold this struct, including alignment padding - #[inline] - pub fn size(&self) -> usize { - self.size as usize - } - - /// Get the struct field at `index`, relative to declaration order. - pub fn get(&self, index: usize) -> &StructField { - &self.fields[index] - } - - /// Get the struct fields as a slice - pub fn fields(&self) -> &[StructField] { - self.fields.as_slice() - } - - /// Returns true if this struct has no fields - pub fn is_empty(&self) -> bool { - self.fields.is_empty() - } - - /// Get the length of this struct (i.e. number of fields) - pub fn len(&self) -> usize { - self.fields.len() - } -} -impl TryFrom for StructType { - type Error = Type; - - fn try_from(ty: Type) -> Result { - match ty { - Type::Struct(ty) => Ok(ty), - other => Err(other), - } - } -} -impl fmt::Display for StructType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.repr { - TypeRepr::Default => f.write_str("struct {")?, - TypeRepr::Transparent => f.write_str("struct #[repr(transparent)] {")?, - TypeRepr::Align(align) => write!(f, "struct #[repr(align({align}))] {{")?, - TypeRepr::Packed(align) => write!(f, "struct #[repr(packed({align}))] {{")?, - }; - for (i, field) in self.fields.iter().enumerate() { - if i > 0 { - write!(f, ", {}", field)?; - } else { - write!(f, "{}", field)?; - } - } - f.write_str("}") - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub struct FunctionType { - pub results: Vec, - pub params: Vec, -} -impl FunctionType { - pub fn new(params: Vec, results: Vec) -> Self { - Self { results, params } - } - - pub fn arity(&self) -> usize { - self.params.len() - } - - pub fn results(&self) -> &[Type] { - self.results.as_slice() - } - - pub fn params(&self) -> &[Type] { - self.params.as_slice() - } -} -impl fmt::Display for FunctionType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use core::fmt::Write; - - f.write_str("fn (")?; - for (i, ty) in self.params.iter().enumerate() { - if i > 0 { - write!(f, ", {}", ty)?; - } else { - write!(f, "{}", ty)?; - } - } - f.write_str(" -> (")?; - for (i, ty) in self.results.iter().enumerate() { - if i > 0 { - write!(f, ", {}", ty)?; - } else { - write!(f, "{}", ty)?; - } - } - f.write_char(')') - } -} - -/// This error is raised when parsing an [AddressSpace] -#[derive(Debug)] -pub enum InvalidAddressSpaceError { - InvalidId, - InvalidIdOverflow, -} -impl fmt::Display for InvalidAddressSpaceError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::InvalidId => { - f.write_str("invalid address space identifier: expected integer value or 'unknown'") - } - Self::InvalidIdOverflow => f.write_str( - "invalid address space identifier: value is too large, expected range is 0..=65535", - ), - } - } -} - -/// This type uniquely identifies the address space associated with a native -/// Miden pointer value -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum AddressSpace { - /// The address space is not known statically, but is available - /// at runtime in the pointer metadata. - /// - /// This is also the type associated with user contexts in Miden, - /// as it cannot be known statically how many such contexts will - /// be used at runtime. - #[default] - Unknown, - /// This address space corresponds to the root context in Miden - /// - /// The root context is the default context in the program entrypoint, - /// and for use cases outside the typical smart contract usage, may be - /// the only context in use at any given time. - /// - /// This address space corresponds to an address space identifier of 0. - Root, - /// This address space corresponds to a statically allocated separate - /// memory region. This can be used to represent things in separate - /// linear memory regions which are accessible simultaneously. - /// - /// Any non-zero identifier can be used for these address spaces. - /// - /// NOTE: It is up to the user to ensure that there are no conflicts - /// between address space identifiers. - Id(NonZeroU16), -} -impl fmt::Display for AddressSpace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Unknown => f.write_str("?"), - Self::Root => f.write_str("0"), - Self::Id(id) => write!(f, "{id}"), - } - } -} -impl FromStr for AddressSpace { - type Err = InvalidAddressSpaceError; - - fn from_str(s: &str) -> Result { - match s { - "unknown" => Ok(Self::Unknown), - id => { - use core::num::IntErrorKind; - match NonZeroU16::from_str(id) { - Ok(id) => Ok(Self::Id(id)), - Err(err) => match err.kind() { - IntErrorKind::Zero => Ok(Self::Root), - IntErrorKind::PosOverflow | IntErrorKind::NegOverflow => { - Err(InvalidAddressSpaceError::InvalidIdOverflow) - } - _ => Err(InvalidAddressSpaceError::InvalidId), - }, - } - } - } - } -} diff --git a/hir/Cargo.toml b/hir/Cargo.toml deleted file mode 100644 index 27bda4a77..000000000 --- a/hir/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "miden-hir" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[build-dependencies] -lalrpop = { version = "0.20", default-features = false } - -[dependencies] -anyhow.workspace = true -cranelift-entity.workspace = true -intrusive-collections.workspace = true -inventory.workspace = true -lalrpop-util="0.20" -miden-assembly.workspace = true -miden-diagnostics.workspace = true -miden-hir-symbol.workspace = true -miden-hir-type.workspace = true -miden-hir-macros.workspace = true -miden-parsing.workspace = true -midenc-session.workspace = true -num-bigint = "0.4" -num-traits = "0.2" -petgraph.workspace = true -paste.workspace = true -rustc-hash.workspace = true -smallvec.workspace = true -thiserror.workspace = true -typed-arena = "2.0" -winter-math = { version = "0.7", default-features = false } - -[dev-dependencies] -pretty_assertions = "1.0" diff --git a/hir/build.rs b/hir/build.rs deleted file mode 100644 index 23c7d3f80..000000000 --- a/hir/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -extern crate lalrpop; - -fn main() { - lalrpop::process_root().unwrap(); -} diff --git a/hir/src/adt/mod.rs b/hir/src/adt/mod.rs deleted file mode 100644 index e5d9caa83..000000000 --- a/hir/src/adt/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod smallmap; -pub mod smallordset; -pub mod smallset; -pub mod sparsemap; - -pub use self::smallmap::SmallMap; -pub use self::smallordset::SmallOrdSet; -pub use self::smallset::SmallSet; -pub use self::sparsemap::{SparseMap, SparseMapValue}; diff --git a/hir/src/adt/smallmap.rs b/hir/src/adt/smallmap.rs deleted file mode 100644 index dd07efe3c..000000000 --- a/hir/src/adt/smallmap.rs +++ /dev/null @@ -1,474 +0,0 @@ -use core::borrow::Borrow; -use core::cmp::Ordering; -use core::fmt; -use core::ops::{Index, IndexMut}; - -use smallvec::SmallVec; - -/// [SmallMap] is a [BTreeMap]-like structure that can store a specified number -/// of elements inline (i.e. on the stack) without allocating memory from the heap. -/// -/// This data structure is designed with two goals in mind: -/// -/// * Support efficient key/value operations over a small set of keys -/// * Preserve the order of keys -/// * Avoid allocating data on the heap for the typical case -/// -/// Internally, [SmallMap] is implemented on top of [SmallVec], and uses binary search -/// to locate elements. This is quite efficient in general, and is particularly fast -/// when all of the data is stored inline, but may not be a good fit for all use cases. -/// -/// Due to its design constraints, it only supports keys which implement [Ord]. -pub struct SmallMap { - items: SmallVec<[KeyValuePair; N]>, -} -impl SmallMap -where - K: Ord, -{ - /// Returns a new, empty [SmallMap] - pub fn new() -> Self { - Self::default() - } - - /// Returns true if this map is empty - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } - - /// Returns the number of key/value pairs in this map - pub fn len(&self) -> usize { - self.items.len() - } - - /// Return an iterator over the key/value pairs in this map - pub fn iter(&self) -> impl DoubleEndedIterator { - self.items.iter().map(|pair| (&pair.key, &pair.value)) - } - - /// Return an iterator over mutable key/value pairs in this map - pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { - self.items - .iter_mut() - .map(|pair| (&pair.key, &mut pair.value)) - } - - /// Returns true if `key` has been inserted in this map - pub fn contains(&self, key: &Q) -> bool - where - K: Borrow, - Q: Ord + ?Sized, - { - self.find(key).is_ok() - } - - /// Returns the value under `key` in this map, if it exists - pub fn get(&self, key: &Q) -> Option<&V> - where - K: Borrow, - Q: Ord + ?Sized, - { - match self.find(key) { - Ok(idx) => Some(&self.items[idx].value), - Err(_) => None, - } - } - - /// Returns a mutable reference to the value under `key` in this map, if it exists - pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> - where - K: Borrow, - Q: Ord + ?Sized, - { - match self.find(key) { - Ok(idx) => Some(&mut self.items[idx].value), - Err(_) => None, - } - } - - /// Inserts a new entry in this map using `key` and `value`. - /// - /// Returns the previous value, if `key` was already present in the map. - pub fn insert(&mut self, key: K, value: V) -> Option { - match self.entry(key) { - Entry::Occupied(mut entry) => Some(core::mem::replace(entry.get_mut(), value)), - Entry::Vacant(entry) => { - entry.insert(value); - None - } - } - } - - /// Removes the value inserted under `key`, if it exists - pub fn remove(&mut self, key: &Q) -> Option - where - K: Borrow, - Q: Ord + ?Sized, - { - match self.find(key) { - Ok(idx) => Some(self.items.remove(idx).value), - Err(_) => None, - } - } - - /// Clear the content of the map - pub fn clear(&mut self) { - self.items.clear(); - } - - /// Returns an [Entry] which can be used to combine `contains`+`insert` type operations. - pub fn entry(&mut self, key: K) -> Entry<'_, K, V, N> { - match self.find(&key) { - Ok(idx) => Entry::occupied(self, idx), - Err(idx) => Entry::vacant(self, idx, key), - } - } - - #[inline] - fn find(&self, item: &Q) -> Result - where - K: Borrow, - Q: Ord + ?Sized, - { - self.items - .binary_search_by(|probe| Ord::cmp(probe.key.borrow(), item)) - } -} -impl Default for SmallMap { - fn default() -> Self { - Self { - items: Default::default(), - } - } -} -impl Eq for SmallMap -where - K: Eq, - V: Eq, -{ -} -impl PartialEq for SmallMap -where - K: PartialEq, - V: PartialEq, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.items - .iter() - .map(|pair| (&pair.key, &pair.value)) - .eq(other.items.iter().map(|pair| (&pair.key, &pair.value))) - } -} -impl fmt::Debug for SmallMap -where - K: fmt::Debug + Ord, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_map() - .entries(self.items.iter().map(|item| (&item.key, &item.value))) - .finish() - } -} -impl Clone for SmallMap -where - K: Clone, - V: Clone, -{ - #[inline] - fn clone(&self) -> Self { - Self { - items: self.items.clone(), - } - } -} -impl IntoIterator for SmallMap -where - K: Ord, -{ - type IntoIter = SmallMapIntoIter; - type Item = (K, V); - - #[inline] - fn into_iter(self) -> Self::IntoIter { - SmallMapIntoIter { - iter: self.items.into_iter(), - } - } -} -impl FromIterator<(K, V)> for SmallMap -where - K: Ord, -{ - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - let mut map = Self::default(); - for (k, v) in iter { - map.insert(k, v); - } - map - } -} -impl Index<&Q> for SmallMap -where - K: Borrow + Ord, - Q: Ord + ?Sized, -{ - type Output = V; - - fn index(&self, key: &Q) -> &Self::Output { - self.get(key).unwrap() - } -} -impl IndexMut<&Q> for SmallMap -where - K: Borrow + Ord, - Q: Ord + ?Sized, -{ - fn index_mut(&mut self, key: &Q) -> &mut Self::Output { - self.get_mut(key).unwrap() - } -} - -#[doc(hidden)] -pub struct SmallMapIntoIter { - iter: smallvec::IntoIter<[KeyValuePair; N]>, -} -impl ExactSizeIterator for SmallMapIntoIter { - #[inline(always)] - fn len(&self) -> usize { - self.iter.len() - } -} -impl Iterator for SmallMapIntoIter { - type Item = (K, V); - - #[inline(always)] - fn next(&mut self) -> Option { - self.iter.next().map(|pair| (pair.key, pair.value)) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn last(self) -> Option<(K, V)> { - self.iter.last().map(|pair| (pair.key, pair.value)) - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.iter.nth(n).map(|pair| (pair.key, pair.value)) - } -} -impl DoubleEndedIterator for SmallMapIntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|pair| (pair.key, pair.value)) - } - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - self.iter.nth_back(n).map(|pair| (pair.key, pair.value)) - } -} - -/// Represents an key/value pair entry in a [SmallMap] -pub enum Entry<'a, K, V, const N: usize> { - Occupied(OccupiedEntry<'a, K, V, N>), - Vacant(VacantEntry<'a, K, V, N>), -} -impl<'a, K, V, const N: usize> Entry<'a, K, V, N> { - fn occupied(map: &'a mut SmallMap, idx: usize) -> Self { - Self::Occupied(OccupiedEntry { map, idx }) - } - - fn vacant(map: &'a mut SmallMap, idx: usize, key: K) -> Self { - Self::Vacant(VacantEntry { map, idx, key }) - } - - pub fn or_insert(self, default: V) -> &'a mut V { - match self { - Self::Occupied(entry) => entry.into_mut(), - Self::Vacant(entry) => entry.insert(default), - } - } - - pub fn or_insert_with(self, default: F) -> &'a mut V - where - F: FnOnce() -> V, - { - match self { - Self::Occupied(entry) => entry.into_mut(), - Self::Vacant(entry) => entry.insert(default()), - } - } - - pub fn key(&self) -> &K { - match self { - Self::Occupied(entry) => entry.key(), - Self::Vacant(entry) => entry.key(), - } - } - - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut V), - { - match self { - Self::Occupied(mut entry) => { - f(entry.get_mut()); - Self::Occupied(entry) - } - vacant @ Self::Vacant(_) => vacant, - } - } -} - -/// Represents an occupied entry in a [SmallMap] -pub struct OccupiedEntry<'a, K, V, const N: usize> { - map: &'a mut SmallMap, - idx: usize, -} -impl<'a, K, V, const N: usize> OccupiedEntry<'a, K, V, N> { - #[inline(always)] - fn get_entry(&self) -> &KeyValuePair { - &self.map.items[self.idx] - } - - pub fn remove_entry(self) -> V { - self.map.items.remove(self.idx).value - } - - pub fn key(&self) -> &K { - &self.get_entry().key - } - - pub fn get(&self) -> &V { - &self.get_entry().value - } - - pub fn get_mut(&mut self) -> &mut V { - &mut self.map.items[self.idx].value - } - - pub fn into_mut(self) -> &'a mut V { - &mut self.map.items[self.idx].value - } -} - -/// Represents a vacant entry in a [SmallMap] -pub struct VacantEntry<'a, K, V, const N: usize> { - map: &'a mut SmallMap, - idx: usize, - key: K, -} -impl<'a, K, V, const N: usize> VacantEntry<'a, K, V, N> { - pub fn key(&self) -> &K { - &self.key - } - - pub fn into_key(self) -> K { - self.key - } - - pub fn insert_with(self, f: F) -> &'a mut V - where - F: FnOnce() -> V, - { - self.map.items.insert( - self.idx, - KeyValuePair { - key: self.key, - value: f(), - }, - ); - &mut self.map.items[self.idx].value - } - - pub fn insert(self, value: V) -> &'a mut V { - self.map.items.insert( - self.idx, - KeyValuePair { - key: self.key, - value, - }, - ); - &mut self.map.items[self.idx].value - } -} - -struct KeyValuePair { - key: K, - value: V, -} -impl AsRef for KeyValuePair { - #[inline] - fn as_ref(&self) -> &V { - &self.value - } -} -impl AsMut for KeyValuePair { - #[inline] - fn as_mut(&mut self) -> &mut V { - &mut self.value - } -} -impl Clone for KeyValuePair -where - K: Clone, - V: Clone, -{ - fn clone(&self) -> Self { - Self { - key: self.key.clone(), - value: self.value.clone(), - } - } -} -impl Copy for KeyValuePair -where - K: Copy, - V: Copy, -{ -} - -impl Eq for KeyValuePair where K: Eq {} - -impl PartialEq for KeyValuePair -where - K: PartialEq, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.key.eq(&other.key) - } -} - -impl Ord for KeyValuePair -where - K: Ord, -{ - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.key.cmp(&other.key) - } -} -impl PartialOrd for KeyValuePair -where - K: PartialOrd, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.key.partial_cmp(&other.key) - } -} diff --git a/hir/src/adt/smallordset.rs b/hir/src/adt/smallordset.rs deleted file mode 100644 index 2c95ca99e..000000000 --- a/hir/src/adt/smallordset.rs +++ /dev/null @@ -1,191 +0,0 @@ -use core::borrow::Borrow; -use core::fmt; - -use smallvec::SmallVec; - -/// [SmallOrdSet] is a [BTreeSet]-like structure that can store a specified number -/// of elements inline (i.e. on the stack) without allocating memory from the heap. -/// -/// This data structure is designed with two goals in mind: -/// -/// * Support efficient set operations over a small set of items -/// * Maintains the underlying set in order (according to the `Ord` impl of the element type) -/// * Avoid allocating data on the heap for the typical case -/// -/// Internally, [SmallOrdSet] is implemented on top of [SmallVec], and uses binary search -/// to locate elements. This is quite efficient in general, and is particularly fast -/// when all of the data is stored inline, but may not be a good fit for all use cases. -/// -/// Due to its design constraints, it only supports elements which implement [Ord]. -/// -/// NOTE: This type differs from [SmallSet] in that [SmallOrdSet] uses the [Ord] implementation -/// of the element type for ordering, while [SmallSet] preserves the insertion order of elements. -/// Beyond that, the two types are meant to be essentially equivalent. -pub struct SmallOrdSet { - items: SmallVec<[T; N]>, -} -impl Default for SmallOrdSet { - fn default() -> Self { - Self { - items: Default::default(), - } - } -} -impl Eq for SmallOrdSet where T: Eq {} -impl PartialEq for SmallOrdSet -where - T: PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - self.items.eq(&other.items) - } -} -impl fmt::Debug for SmallOrdSet -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_set().entries(self.items.iter()).finish() - } -} -impl Clone for SmallOrdSet -where - T: Clone, -{ - #[inline] - fn clone(&self) -> Self { - Self { - items: self.items.clone(), - } - } -} -impl SmallOrdSet -where - T: Ord, -{ - pub fn from_vec(items: SmallVec<[T; N]>) -> Self { - let mut set = Self { items }; - set.sort_and_dedup(); - set - } - - #[inline] - pub fn from_buf(buf: [T; N]) -> Self { - Self::from_vec(buf.into()) - } -} -impl IntoIterator for SmallOrdSet { - type IntoIter = smallvec::IntoIter<[T; N]>; - type Item = T; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.items.into_iter() - } -} -impl FromIterator for SmallOrdSet -where - T: Ord, -{ - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - let mut set = Self::default(); - for item in iter { - set.insert(item); - } - set - } -} -impl SmallOrdSet -where - T: Ord, -{ - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } - - pub fn len(&self) -> usize { - self.items.len() - } - - pub fn as_slice(&self) -> &[T] { - self.items.as_slice() - } - - pub fn iter(&self) -> core::slice::Iter<'_, T> { - self.items.iter() - } - - pub fn insert(&mut self, item: T) -> bool { - match self.find(&item) { - Ok(_) => false, - Err(idx) => { - self.items.insert(idx, item); - true - } - } - } - - pub fn remove(&mut self, item: &Q) -> Option - where - T: Borrow, - Q: Ord + ?Sized, - { - match self.find(item) { - Ok(idx) => Some(self.items.remove(idx)), - Err(_) => None, - } - } - - /// Clear the content of the set - pub fn clear(&mut self) { - self.items.clear(); - } - - pub fn contains(&self, item: &Q) -> bool - where - T: Borrow, - Q: Ord + ?Sized, - { - self.find(item).is_ok() - } - - pub fn get(&self, item: &Q) -> Option<&T> - where - T: Borrow, - Q: Ord + ?Sized, - { - match self.find(item) { - Ok(idx) => Some(&self.items[idx]), - Err(_) => None, - } - } - - pub fn get_mut(&mut self, item: &Q) -> Option<&mut T> - where - T: Borrow, - Q: Ord + ?Sized, - { - match self.find(item) { - Ok(idx) => Some(&mut self.items[idx]), - Err(_) => None, - } - } - - #[inline] - fn find(&self, item: &Q) -> Result - where - T: Borrow, - Q: Ord + ?Sized, - { - self.items - .binary_search_by(|probe| Ord::cmp(probe.borrow(), item)) - } - - fn sort_and_dedup(&mut self) { - self.items.sort_unstable(); - self.items.dedup(); - } -} diff --git a/hir/src/adt/smallset.rs b/hir/src/adt/smallset.rs deleted file mode 100644 index d0692ef2f..000000000 --- a/hir/src/adt/smallset.rs +++ /dev/null @@ -1,181 +0,0 @@ -use core::borrow::Borrow; -use core::fmt; - -use smallvec::SmallVec; - -/// [SmallSet] is a set data structure that can store a specified number -/// of elements inline (i.e. on the stack) without allocating memory from the heap. -/// -/// This data structure is designed with two goals in mind: -/// -/// * Support efficient set operations over a small set of items -/// * Preserve the insertion order of those items -/// * Avoid allocating data on the heap for the typical case -/// -/// Internally, [SmallSet] is implemented on top of [SmallVec], and uses linear search -/// to locate elements. This is only reasonably efficient on small sets, for anything -/// larger you should reach for quite efficient in general, and is particularly fast -/// when all of the data is stored inline, but may not be a good fit for all use cases. -/// -/// Due to its design constraints, it only supports elements which implement [Ord]. -pub struct SmallSet { - items: SmallVec<[T; N]>, -} -impl Default for SmallSet { - fn default() -> Self { - Self { - items: Default::default(), - } - } -} -impl Eq for SmallSet where T: Eq {} -impl PartialEq for SmallSet -where - T: PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - self.items.eq(&other.items) - } -} -impl fmt::Debug for SmallSet -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_set().entries(self.items.iter()).finish() - } -} -impl Clone for SmallSet -where - T: Clone, -{ - #[inline] - fn clone(&self) -> Self { - Self { - items: self.items.clone(), - } - } -} -impl IntoIterator for SmallSet { - type IntoIter = smallvec::IntoIter<[T; N]>; - type Item = T; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.items.into_iter() - } -} -impl From<[T; N]> for SmallSet -where - T: Ord, -{ - #[inline] - fn from(items: [T; N]) -> Self { - Self::from_iter(items) - } -} -impl FromIterator for SmallSet -where - T: Ord, -{ - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - let mut set = Self::default(); - for item in iter { - set.insert(item); - } - set - } -} -impl SmallSet -where - T: Ord, -{ - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } - - pub fn len(&self) -> usize { - self.items.len() - } - - pub fn iter(&self) -> core::slice::Iter<'_, T> { - self.items.iter() - } - - #[inline] - pub fn as_slice(&self) -> &[T] { - self.items.as_slice() - } - - pub fn insert(&mut self, item: T) -> bool { - if self.contains(&item) { - return false; - } - self.items.push(item); - true - } - - pub fn remove(&mut self, item: &Q) -> Option - where - T: Borrow, - Q: Ord + ?Sized, - { - match self.find(item) { - Some(idx) => Some(self.items.remove(idx)), - None => None, - } - } - - /// Clear the content of the set - pub fn clear(&mut self) { - self.items.clear(); - } - - pub fn contains(&self, item: &Q) -> bool - where - T: Borrow, - Q: Ord + ?Sized, - { - self.find(item).is_some() - } - - pub fn get(&self, item: &Q) -> Option<&T> - where - T: Borrow, - Q: Ord + ?Sized, - { - match self.find(item) { - Some(idx) => Some(&self.items[idx]), - None => None, - } - } - - pub fn get_mut(&mut self, item: &Q) -> Option<&mut T> - where - T: Borrow, - Q: Ord + ?Sized, - { - match self.find(item) { - Some(idx) => Some(&mut self.items[idx]), - None => None, - } - } - - /// Convert this set into a `SmallVec` containing the items of the set - #[inline] - pub fn into_vec(self) -> SmallVec<[T; N]> { - self.items - } - - #[inline] - fn find(&self, item: &Q) -> Option - where - T: Borrow, - Q: Ord + ?Sized, - { - self.items.iter().position(|elem| elem.borrow() == item) - } -} diff --git a/hir/src/adt/sparsemap.rs b/hir/src/adt/sparsemap.rs deleted file mode 100644 index e0e56c882..000000000 --- a/hir/src/adt/sparsemap.rs +++ /dev/null @@ -1,241 +0,0 @@ -//! This module is based on [cranelift_entity::SparseMap], but implemented in-tree -//! because the SparseMapValueTrait is not implemented for any standard library types -use cranelift_entity::{EntityRef, SecondaryMap}; - -pub trait SparseMapValue { - fn key(&self) -> K; -} -impl> SparseMapValue for Box { - fn key(&self) -> K { - (**self).key() - } -} -impl> SparseMapValue for std::rc::Rc { - fn key(&self) -> K { - (**self).key() - } -} -impl SparseMapValue for crate::Value { - fn key(&self) -> crate::Value { - *self - } -} -impl SparseMapValue for crate::Inst { - fn key(&self) -> crate::Inst { - *self - } -} -impl SparseMapValue for crate::Block { - fn key(&self) -> crate::Block { - *self - } -} - -/// A sparse mapping of entity references. -/// -/// A `SparseMap` map provides: -/// -/// - Memory usage equivalent to `SecondaryMap` + `Vec`, so much smaller than -/// `SecondaryMap` for sparse mappings of larger `V` types. -/// - Constant time lookup, slightly slower than `SecondaryMap`. -/// - A very fast, constant time `clear()` operation. -/// - Fast insert and erase operations. -/// - Stable iteration that is as fast as a `Vec`. -/// -/// # Compared to `SecondaryMap` -/// -/// When should we use a `SparseMap` instead of a secondary `SecondaryMap`? First of all, -/// `SparseMap` does not provide the functionality of a `PrimaryMap` which can allocate and assign -/// entity references to objects as they are pushed onto the map. It is only the secondary entity -/// maps that can be replaced with a `SparseMap`. -/// -/// - A secondary entity map assigns a default mapping to all keys. It doesn't distinguish between -/// an unmapped key and one that maps to the default value. `SparseMap` does not require -/// `Default` values, and it tracks accurately if a key has been mapped or not. -/// - Iterating over the contents of an `SecondaryMap` is linear in the size of the *key space*, -/// while iterating over a `SparseMap` is linear in the number of elements in the mapping. This -/// is an advantage precisely when the mapping is sparse. -/// - `SparseMap::clear()` is constant time and super-fast. `SecondaryMap::clear()` is linear in -/// the size of the key space. (Or, rather the required `resize()` call following the `clear()` -/// is). -/// - `SparseMap` requires the values to implement `SparseMapValue` which means that they must -/// contain their own key. -pub struct SparseMap -where - K: EntityRef, - V: SparseMapValue, -{ - sparse: SecondaryMap, - dense: Vec, -} -impl Default for SparseMap -where - K: EntityRef, - V: SparseMapValue, -{ - fn default() -> Self { - Self { - sparse: SecondaryMap::new(), - dense: Vec::new(), - } - } -} -impl core::fmt::Debug for SparseMap -where - K: EntityRef + core::fmt::Debug, - V: SparseMapValue + core::fmt::Debug, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_map() - .entries(self.values().map(|v| (v.key(), v))) - .finish() - } -} -impl SparseMap -where - K: EntityRef, - V: SparseMapValue, -{ - /// Create a new empty mapping. - #[inline] - pub fn new() -> Self { - Self::default() - } - - /// Returns the number of elements in the map. - pub fn len(&self) -> usize { - self.dense.len() - } - - /// Returns true is the map contains no elements. - pub fn is_empty(&self) -> bool { - self.dense.is_empty() - } - - /// Remove all elements from the mapping. - pub fn clear(&mut self) { - self.dense.clear(); - } - - /// Returns a reference to the value corresponding to the key. - pub fn get(&self, key: K) -> Option<&V> { - if let Some(idx) = self.sparse.get(key).cloned() { - if let Some(entry) = self.dense.get(idx as usize) { - if entry.key() == key { - return Some(entry); - } - } - } - None - } - - /// Returns a mutable reference to the value corresponding to the key. - /// - /// Note that the returned value must not be mutated in a way that would change its key. This - /// would invalidate the sparse set data structure. - pub fn get_mut(&mut self, key: K) -> Option<&mut V> { - if let Some(idx) = self.sparse.get(key).cloned() { - if let Some(entry) = self.dense.get_mut(idx as usize) { - if entry.key() == key { - return Some(entry); - } - } - } - None - } - - /// Return the index into `dense` of the value corresponding to `key`. - fn index(&self, key: K) -> Option { - if let Some(idx) = self.sparse.get(key).cloned() { - let idx = idx as usize; - if let Some(entry) = self.dense.get(idx) { - if entry.key() == key { - return Some(idx); - } - } - } - None - } - - /// Return `true` if the map contains a value corresponding to `key`. - pub fn contains_key(&self, key: K) -> bool { - self.get(key).is_some() - } - - /// Insert a value into the map. - /// - /// If the map did not have this key present, `None` is returned. - /// - /// If the map did have this key present, the value is updated, and the old value is returned. - /// - /// It is not necessary to provide a key since the value knows its own key already. - pub fn insert(&mut self, value: V) -> Option { - let key = value.key(); - - // Replace the existing entry for `key` if there is one. - if let Some(entry) = self.get_mut(key) { - return Some(core::mem::replace(entry, value)); - } - - // There was no previous entry for `key`. Add it to the end of `dense`. - let idx = self.dense.len(); - debug_assert!(idx <= u32::MAX as usize, "SparseMap overflow"); - self.dense.push(value); - self.sparse[key] = idx as u32; - None - } - - /// Remove a value from the map and return it. - pub fn remove(&mut self, key: K) -> Option { - if let Some(idx) = self.index(key) { - let back = self.dense.pop().unwrap(); - - // Are we popping the back of `dense`? - if idx == self.dense.len() { - return Some(back); - } - - // We're removing an element from the middle of `dense`. - // Replace the element at `idx` with the back of `dense`. - // Repair `sparse` first. - self.sparse[back.key()] = idx as u32; - return Some(core::mem::replace(&mut self.dense[idx], back)); - } - - // Nothing to remove. - None - } - - /// Remove the last value from the map. - pub fn pop(&mut self) -> Option { - self.dense.pop() - } - - /// Get an iterator over the values in the map. - /// - /// The iteration order is entirely determined by the preceding sequence of `insert` and - /// `remove` operations. In particular, if no elements were removed, this is the insertion - /// order. - pub fn values(&self) -> core::slice::Iter { - self.dense.iter() - } - - /// Get the values as a slice. - pub fn as_slice(&self) -> &[V] { - self.dense.as_slice() - } -} - -/// Iterating over the elements of a set. -impl<'a, K, V> IntoIterator for &'a SparseMap -where - K: EntityRef, - V: SparseMapValue, -{ - type Item = &'a V; - type IntoIter = core::slice::Iter<'a, V>; - - fn into_iter(self) -> Self::IntoIter { - self.values() - } -} diff --git a/hir/src/asm/builder.rs b/hir/src/asm/builder.rs deleted file mode 100644 index 2beb21e66..000000000 --- a/hir/src/asm/builder.rs +++ /dev/null @@ -1,2090 +0,0 @@ -use crate::{ - CallConv, DataFlowGraph, Felt, FunctionIdent, Inst, InstBuilder, Instruction, Overflow, - SourceSpan, Type, Value, -}; - -use smallvec::smallvec; - -use super::*; - -/// Used to construct an [InlineAsm] instruction, while checking the input/output types, -/// and enforcing various safety invariants. -pub struct MasmBuilder { - /// The [InstBuilderBase] which we are building from - builder: B, - /// The span of the resulting inline assembly block - span: SourceSpan, - /// The inline assembly block we're building - asm: InlineAsm, - /// The current code block in the inline assembly that the builder is inserting into - current_block: MasmBlockId, - /// The emulated operand stack, primarily used to track the number of stack elements - /// upon entry and exit from the inline assembly block. - /// - /// The only `Type` which is represented on this stack is `Type::Felt`, since we're only - /// interested in the number of stack elements at any given point. In the future, we may - /// choose to do additional type checking. - /// - /// Upon exit from the inline assembly block, the state of the stack must have enough elements - /// to store a value of the expected result type, represented by `ty`. Whether those elements - /// actually store a valid value of that type is beyond the scope of this builder, for now. - stack: OperandStack, -} -impl<'f, B: InstBuilder<'f>> MasmBuilder { - /// Construct a new inline assembly builder in the function represented by `dfg`, to be inserted at `ip`. - /// - /// The `args` list represents the arguments which will be visible on the operand stack in this inline assembly block. - /// - /// The `results` set represents the types that are expected to be found on the operand stack when the inline - /// assembly block finishes executing. Use an empty set to represent no results. - /// - /// Any attempt to modify the operand stack beyond what is made visible via arguments, or introduced within the - /// inline assembly block, will cause an assertion to fail. - pub fn new(mut builder: B, args: &[Value], results: Vec, span: SourceSpan) -> Self { - // Construct the initial operand stack with the given arguments - let mut stack = OperandStack::::default(); - { - let dfg = builder.data_flow_graph(); - for arg in args.iter().rev().copied() { - stack.push(dfg.value_type(arg).clone()); - } - } - - // Construct an empty inline assembly block with the given arguments - let mut asm = InlineAsm::new(results); - { - let dfg = builder.data_flow_graph_mut(); - let mut vlist = ValueList::default(); - vlist.extend(args.iter().copied(), &mut dfg.value_lists); - asm.args = vlist; - } - - let current_block = asm.body; - Self { - builder, - span, - asm, - current_block, - stack, - } - } - - /// Create a new, empty MASM code block, for use with control flow instructions - #[inline] - pub fn create_block(&mut self) -> MasmBlockId { - self.asm.create_block() - } - - /// Change the insertion point of the builder to the end of `block` - #[inline(always)] - pub fn switch_to_block(&mut self, block: MasmBlockId) { - self.current_block = block; - } - - /// Get a builder for a single MASM instruction - pub fn ins<'a, 'b: 'a>(&'b mut self) -> MasmOpBuilder<'a> { - MasmOpBuilder { - dfg: self.builder.data_flow_graph_mut(), - asm: &mut self.asm, - stack: &mut self.stack, - ip: self.current_block, - } - } - - /// Finalize this inline assembly block, inserting it into the `Function` from which this builder was derived. - /// - /// Returns the [Inst] which corresponds to the inline assembly instruction, and the inner [DataFlowGraph] reference - /// held by the underlying [InstBuilderBase]. - pub fn build(self) -> Inst { - if self.asm.results.is_empty() { - assert!(self.stack.is_empty(), "invalid inline assembly: expected operand stack to be empty upon exit, found: {:?}", self.stack.debug()); - } else { - let mut len = 0; - for ty in self.asm.results.iter() { - len += ty.size_in_felts(); - } - assert_eq!( - len, - self.stack.len(), - "invalid inline assembly: needed {} elements on the operand stack, found: {:?}", - len, - self.stack.debug() - ); - } - - let span = self.span; - let data = Instruction::InlineAsm(self.asm); - self.builder.build(data, Type::Unit, span).0 - } -} - -/// Used to construct a single MASM opcode -pub struct MasmOpBuilder<'a> { - dfg: &'a mut DataFlowGraph, - /// The inline assembly block being created - asm: &'a mut InlineAsm, - /// The state of the operand stack at this point in the program - stack: &'a mut OperandStack, - /// The block to which this builder should append the instruction it builds - ip: MasmBlockId, -} -impl<'a> MasmOpBuilder<'a> { - /// Pads the stack with four zero elements - pub fn padw(mut self) { - self.build(self.ip, MasmOp::Padw); - } - - /// Pushes an element on the stack - pub fn push(mut self, imm: Felt) { - self.build(self.ip, MasmOp::Push(imm)); - } - - /// Pushes a word on the stack - pub fn pushw(mut self, word: [Felt; 4]) { - self.build(self.ip, MasmOp::Pushw(word)); - } - - /// 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)); - } - - /// 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)); - } - - /// 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)); - } - - /// Drops the element on the top of the stack - pub fn drop(mut self) { - self.build(self.ip, MasmOp::Drop); - } - - /// Drops the word (first four elements) on the top of the stack - pub fn dropw(mut self) { - self.build(self.ip, MasmOp::Dropw); - } - - /// Duplicates the `n`th element from the top of the stack, to the top of the stack - /// - /// 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)); - } - - /// Duplicates the `n`th word from the top of the stack, to the top of the stack - /// - /// 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)); - } - - /// 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)); - } - - /// 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)); - } - - /// 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)); - } - - /// 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)); - } - - /// 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)); - } - - /// 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)); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// Pops the top element on the stack, and traps if that element is != 1. - pub fn assert(mut self, error_code: Option) { - let op = error_code - .map(MasmOp::AssertWithError) - .unwrap_or(MasmOp::Assert); - self.build(self.ip, op); - } - - /// Pops the top element on the stack, and traps if that element is != 0. - pub fn assertz(mut self, error_code: Option) { - let op = error_code - .map(MasmOp::AssertzWithError) - .unwrap_or(MasmOp::Assertz); - self.build(self.ip, op); - } - - /// Pops the top two elements on the stack, and traps if they are not equal. - pub fn assert_eq(mut self, error_code: Option) { - let op = error_code - .map(MasmOp::AssertEqWithError) - .unwrap_or(MasmOp::AssertEq); - self.build(self.ip, op); - } - - /// Pops the top two words on the stack, and traps if they are not equal. - pub fn assert_eqw(mut self, error_code: Option) { - let op = error_code - .map(MasmOp::AssertEqwWithError) - .unwrap_or(MasmOp::AssertEqw); - self.build(self.ip, op); - } - - /// 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); - } - - /// 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)); - } - - /// Pops an element containing a memory address + element offset from the top of the stack, - /// and loads the element of the word at that address + offset to the top of the stack. - /// - /// NOTE: This is an experimental instruction which is not implemented in Miden VM yet. - pub fn load_offset(mut self) { - self.build(self.ip, MasmOp::MemLoadOffset); - } - - /// Loads the element of the word at the given address and element offset to the top of the stack. - /// - /// NOTE: This is an experimental instruction which is not implemented in Miden VM yet. - pub fn load_offset_imm(mut self, addr: u32, offset: u8) { - assert!( - offset < 4, - "invalid element offset, must be in the range 0..=3, got {}", - offset - ); - self.build(self.ip, MasmOp::MemLoadOffsetImm(addr, offset)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// Pops two elements, the first containing a memory address + element offset from the - /// top of the stack, the second the value to be stored to the word at that address, - /// using the offset to determine which element will be written to. - pub fn store_offset(mut self) { - self.build(self.ip, MasmOp::MemStoreOffset); - } - - /// Pops an element from the top of the stack, and stores it at the given offset of - /// the word at the given address. - pub fn store_offset_imm(mut self, addr: u32, offset: u8) { - assert!( - offset < 4, - "invalid element offset, must be in the range 0..=3, got {}", - offset - ); - self.build(self.ip, MasmOp::MemStoreOffsetImm(addr, offset)); - } - - /// 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); - } - - /// 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)); - } - - /// Begins construction of a `if.true` statement. - /// - /// An `if.true` pops a boolean value off the stack, and uses it to choose between - /// one of two branches. The "then" branch is taken if the conditional is true, - /// the "else" branch otherwise. - /// - /// NOTE: This function will panic if the top of the operand stack is not of boolean type - /// when called. - /// - /// You must ensure that both branches of the `if.true` statement leave the operand stack - /// 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> { - 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(); - IfTrueBuilder { - dfg: self.dfg, - asm: self.asm, - in_stack: self.stack, - out_stack, - ip: self.ip, - then_blk: None, - else_blk: None, - } - } - - /// Begins construction of a `while.true` loop. - /// - /// A `while.true` pops a boolean value off the stack to use as the condition for - /// entering the loop, and will then execute the loop body for as long as the value - /// on top of the stack is a boolean and true. If the condition is not a boolean, - /// execution traps. - /// - /// NOTE: This function will panic if the top of the operand stack is not of boolean type - /// when called. - /// - /// Before finalizing construction of the loop body, you must ensure two things: - /// - /// 1. There is a value of boolean type on top of the operand stack - /// 2. The abstract state of the operand stack, assuming the boolean just mentioned - /// has been popped, must be consistent with the state of the operand stack when the - /// loop was entered, as well as if the loop was skipped due to the conditional being - /// false. The abstract state referred to here is the number, and type, of the elements - /// 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> { - 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(); - let body = self.asm.create_block(); - LoopBuilder { - dfg: self.dfg, - asm: self.asm, - in_stack: self.stack, - out_stack, - ip: self.ip, - body, - style: LoopType::While, - } - } - - /// Begins construction of a `repeat` loop, with an iteration count of `n`. - /// - /// A `repeat` instruction requires no operands on the stack, and will execute the loop body `n` times. - /// - /// NOTE: The iteration count must be non-zero, or this function will panic. - pub fn repeat(self, n: u8) -> 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(); - LoopBuilder { - dfg: self.dfg, - asm: self.asm, - in_stack: self.stack, - out_stack, - ip: self.ip, - body, - style: LoopType::Repeat(n), - } - } - - /// Executes the named procedure as a regular function. - pub fn exec(mut self, id: FunctionIdent) { - self.build(self.ip, MasmOp::Exec(id)); - } - - /// 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) - } - - /// Executes the named procedure as a syscall. - pub fn syscall(mut self, id: FunctionIdent) { - self.build(self.ip, MasmOp::Syscall(id)); - } - - /// 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)) - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// Negates the field element on top of the stack - pub fn neg(mut self) { - self.build(self.ip, MasmOp::Neg); - } - - /// 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); - } - - /// Increments the field element on top of the stack - pub fn incr(mut self) { - self.build(self.ip, MasmOp::Incr); - } - - /// 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); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// Pushes the current value of the cycle counter (clock) on the stack - pub fn clk(mut self) { - self.build(self.ip, MasmOp::Clk); - } - - /// Pushes the hash of the caller on the stack - pub fn caller(mut self) { - self.build(self.ip, MasmOp::Caller); - } - - /// 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); - } - - /// 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); - } - - /// 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) { - let op = error_code - .map(MasmOp::U32AssertWithError) - .unwrap_or(MasmOp::U32Assert); - self.build(self.ip, op); - } - - /// 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) { - let op = error_code - .map(MasmOp::U32Assert2WithError) - .unwrap_or(MasmOp::U32Assert2); - self.build(self.ip, op); - } - - /// 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) { - let op = error_code - .map(MasmOp::U32AssertwWithError) - .unwrap_or(MasmOp::U32Assertw); - self.build(self.ip, op); - } - - /// 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); - } - - /// Pops an element, `a`, from the stack, and splits it into two elements, `b` and `c`, each of which are a valid u32 value. - /// - /// 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); - } - - /// 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) { - let op = match overflow { - Overflow::Unchecked => MasmOp::Add, - Overflow::Checked => { - return self.build_many( - self.ip, - [MasmOp::U32Assert2, MasmOp::Add, MasmOp::U32Assert], - ); - } - Overflow::Overflowing => MasmOp::U32OverflowingAdd, - Overflow::Wrapping => MasmOp::U32WrappingAdd, - }; - self.build(self.ip, op); - } - - /// Same as above, but `a` is provided by the given immediate. - pub fn add_imm_u32(mut self, imm: u32, overflow: Overflow) { - 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, - ], - ); - } - Overflow::Overflowing => MasmOp::U32OverflowingAddImm(imm), - Overflow::Wrapping => MasmOp::U32WrappingAddImm(imm), - }; - self.build(self.ip, op); - } - - /// 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); - } - - /// 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); - } - - /// 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) { - let op = match overflow { - Overflow::Unchecked => MasmOp::Sub, - Overflow::Checked => { - return self.build_many( - self.ip, - [MasmOp::U32Assert2, MasmOp::Sub, MasmOp::U32Assert], - ); - } - Overflow::Overflowing => MasmOp::U32OverflowingSub, - Overflow::Wrapping => MasmOp::U32WrappingSub, - }; - self.build(self.ip, op); - } - - /// Same as above, but `a` is provided by the given immediate. - pub fn sub_imm_u32(mut self, imm: u32, overflow: Overflow) { - 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, - ], - ); - } - Overflow::Overflowing => MasmOp::U32OverflowingSubImm(imm), - Overflow::Wrapping => MasmOp::U32WrappingSubImm(imm), - }; - self.build(self.ip, op); - } - - /// 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) { - let op = match overflow { - Overflow::Unchecked => MasmOp::Mul, - Overflow::Checked => { - return self.build_many( - self.ip, - [MasmOp::U32Assert2, MasmOp::Mul, MasmOp::U32Assert], - ); - } - Overflow::Overflowing => MasmOp::U32OverflowingMul, - Overflow::Wrapping => MasmOp::U32WrappingMul, - }; - self.build(self.ip, op); - } - - /// Same as above, but `a` is provided by the given immediate. - pub fn mul_imm_u32(mut self, imm: u32, overflow: Overflow) { - 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, - ], - ); - } - Overflow::Overflowing => MasmOp::U32OverflowingMulImm(imm), - Overflow::Wrapping => MasmOp::U32WrappingMulImm(imm), - }; - self.build(self.ip, op); - } - - /// 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); - } - - /// 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); - } - - /// Performs unsigned division of the top two elements on the stack, `b` and `a` respectively, which - /// are expected to be valid u32 values. - /// - /// 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); - } - - /// 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)); - } - - /// Pops two elements off the stack, `b` and `a` respectively, and computes `a mod b`. - /// - /// 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); - } - - /// 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)); - } - - /// Pops two elements off the stack, `b` and `a` respectively, and computes `a / b`, and `a mod b`, - /// pushing the results of each on the stack in that order. - /// - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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)); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - /// 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); - } - - #[inline(never)] - fn build(&mut self, ip: MasmBlockId, op: MasmOp) { - apply_op_stack_effects(&op, self.stack, self.dfg, self.asm); - self.asm.push(ip, op); - } - - #[inline(never)] - 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); - } - } -} - -#[doc(hidden)] -enum IfBranch { - Then, - Else, -} - -/// This builder is used to construct an `if.true` instruction, while maintaining -/// the invariant that the operand stack has a uniform state upon exit from either -/// branch of the `if.true`, i.e. the number of elements, and their types, must -/// match. -/// -/// We do this by snapshotting the state of the operand stack on entry, using it -/// when visiting each branch as the initial stack state, and then validating that -/// when both branches have been constructed, that the stack state on exit is the -/// same. The first branch to be completed defines the expected state of the stack -/// for the remaining branch. -/// -/// # Example -/// -/// The general usage here looks like this, where `masm_builder` is an instance of -/// [MasmBuilder]: -/// -/// ```text,ignore -/// // If the current top of the stack is > 0, decrement the next stack element, which -/// is a counter, and then call a function, otherwise, pop the counter, push 0, and proceed. -/// masm_builder.ins().gt_imm(Felt::ZERO); -/// let if_builder = masm_builder.ins().if_true(); -/// -/// // Build the then branch -/// let then_b = if_builder.build_then(); -/// then_b.ins().sub_imm(Felt::new(1 as u64)); -/// then_b.ins().exec("do_some_stuff_and_return_a_boolean".parse().unwrap()); -/// then_b.end(); -/// -/// // Build the else branch -/// let else_b = if_builder.build_else(); -/// else_b.ins().pop(); -/// else_b.ins().push(Felt::ZERO); -/// else_b.end(); -/// -/// // Finalize -/// if_builder.build(); -/// ``` -pub struct IfTrueBuilder<'a> { - dfg: &'a mut DataFlowGraph, - asm: &'a mut InlineAsm, - /// This reference is to the operand stack in the parent [MasmOpBuilder], - /// which represents the operand stack on entry to the `if.true`. Upon - /// finalizatio of the `if.true`, we use update this operand stack to - /// reflect the state upon exit from the `if.true`. - /// - /// In effect, when the call to `if_true` returns, the operand stack in the - /// parent builder will look as if the `if.true` instruction has finished executing. - in_stack: &'a mut OperandStack, - /// This is set when the first branch is finished being constructed, and - /// 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, - /// 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 - then_blk: Option, - /// The block id for the else branch, unset until it has been finalized - else_blk: Option, -} -impl<'f> IfTrueBuilder<'f> { - /// Start constructing the then block for this `if.true` instruction - /// - /// NOTE: This function will panic if the then block has already been built - pub fn build_then<'a: 'f, 'b: 'f + 'a>(&'b mut self) -> IfTrueBlockBuilder<'a> { - assert!( - self.then_blk.is_none(), - "cannot build the 'then' branch twice" - ); - let then_blk = self.asm.create_block(); - let stack = self.in_stack.clone(); - IfTrueBlockBuilder { - builder: self, - stack, - block: then_blk, - branch: IfBranch::Then, - } - } - - /// Start constructing the else block for this `if.true` instruction - /// - /// NOTE: This function will panic if the else block has already been built - pub fn build_else<'a: 'f, 'b: 'f + 'a>(&'b mut self) -> IfTrueBlockBuilder<'a> { - assert!( - self.else_blk.is_none(), - "cannot build the 'else' branch twice" - ); - let else_blk = self.asm.create_block(); - let stack = self.in_stack.clone(); - IfTrueBlockBuilder { - builder: self, - stack, - block: else_blk, - branch: IfBranch::Else, - } - } - - /// Finalize this `if.true` instruction, inserting it into the block this - /// builder was constructed from. - 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)); - // 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(); - in_stack.append(self.out_stack.stack_mut()); - } -} - -/// Used to construct a single branch of an `if.true` instruction -/// -/// See [IfTrueBuilder] for usage. -pub struct IfTrueBlockBuilder<'a> { - builder: &'a mut IfTrueBuilder<'a>, - // The state of the operand stack in this block - stack: OperandStack, - // The block we're building - block: MasmBlockId, - branch: IfBranch, -} -impl<'f> IfTrueBlockBuilder<'f> { - /// Construct a MASM instruction in this block - pub fn ins<'a, 'b: 'a>(&'b mut self) -> MasmOpBuilder<'a> { - MasmOpBuilder { - dfg: self.builder.dfg, - asm: self.builder.asm, - stack: &mut self.stack, - ip: self.block, - } - } - - /// Finalize this block, and release the builder - pub fn end(self) {} -} -impl<'a> Drop for IfTrueBlockBuilder<'a> { - fn drop(&mut self) { - match self.branch { - IfBranch::Then => { - self.builder.then_blk = Some(self.block); - } - IfBranch::Else => { - self.builder.else_blk = Some(self.block); - } - } - - // If the if.true instruction is complete, validate that the operand stack in - // both branches is identical - // - // Otherwise, save the state of the stack here to be compared to the other - // branch when it is constructed - let is_complete = self.builder.then_blk.is_some() && self.builder.else_blk.is_some(); - if is_complete { - assert_eq!(self.stack.stack(), self.builder.out_stack.stack(), "expected the operand stack to be in the same abstract state upon exit from either branch of this if.true instruction"); - } else { - core::mem::swap(&mut self.builder.out_stack, &mut self.stack); - } - } -} - -#[doc(hidden)] -enum LoopType { - While, - Repeat(u8), -} - -/// This builder is used to construct both `while.true` and `repeat.n` loops, enforcing -/// their individual invariants with regard to the operand stack. -/// -/// In particular, this builder ensures that the body of a `while.true` loop is valid, -/// i.e. that when returning to the top of the loop to evaluate the conditional, that -/// there is a boolean value on top of the stack for that purpose. Similarly, it validates -/// that after the conditional has been evaluated, that the abstract state of the operand -/// stack is the same across iterations, and regardless of whether the loop is taken. The -/// abstract state in question is the number, and type, of the operands on the stack. -/// -/// # Example -/// -/// The general usage here looks like this, where `masm_builder` is an instance of -/// [MasmBuilder]: -/// -/// ```text,ignore -/// // For our example here, we're generating inline assembly that performs -/// // the equivalent of `for (i = 0; i < len; i++) sum += array[i / 4][i % 4]`, -/// // where `array` is a pointer to words, and we're attempting to sum `len` -/// // field elements, across how ever many words that spans. -/// // -/// // We assume the operand stack is as follows (top to bottom): -/// // -/// // [len, sum, array] -/// // -/// // First, build out the loop header -/// masm_builder.ins().push(Felt::ZERO); // [i, len, sum, array] -/// masm_builder.ins().dup(0); // [i, i, len, sum, array] -/// masm_builder.ins().dup(2); // [len, i, i, len, sum, array] -/// masm_builder.ins().lt(); // [i < len, i, len, sum, array] -/// -/// // Now, build the loop body -/// // -/// // The state of the stack on entry is: [i, len, sum, array] -/// let mut lb = masm_builder.ins().while_true(); -/// -/// // Calculate `i / 4` -/// lb.ins().dup(0); // [i, i, len, sum, array] -/// lb.ins().div_imm(4); // [word_offset, i, len, sum, array] -/// -/// // Calculate the address for `array[i / 4]` -/// lb.ins().dup(4); // [array, word_offset, ..] -/// lb.ins().add_u32(Overflow::Checked); // [array + word_offset, i, ..] -/// -/// // Calculate the `i % 4` -/// lb.ins().dup(1); // [i, array + word_offset, ..] -/// lb.ins().mod_imm_u32(4); // [element_offset, array + word_offset, ..] -/// -/// // Precalculate what elements of the word to drop, so that -/// // we are only left with the specific element we wanted -/// lb.ins().dup(0); // [element_offset, element_offset, ..] -/// lb.ins().lt_imm(Felt::new(3)); // [element_offset < 3, element_offset, ..] -/// lb.ins().dup(1); // [element_offset, element_offset < 3, ..] -/// lb.ins().lt_imm(Felt::new(2)); // [element_offset < 2, element_offset < 3, ..] -/// lb.ins().dup(2); // [element_offset, element_offset < 2, ..] -/// lb.ins().lt_imm(Felt::new(1)); // [element_offset < 1, element_offset < 2, ..] -/// -/// // Load the word -/// lb.ins().dup(4); // [array + word_offset, element_offset < 1] -/// lb.ins().loadw(); // [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, len, sum, array] -/// lb.ins().movup(3); // [sum, array[i], i, len, array] -/// lb.ins().add(); // [sum + array[i], i, len, array] -/// lb.ins().movdn(2); // [i, len, sum + array[i], array] -/// -/// // 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, len, ..] -/// lb.ins().dup(2); // [len, i, i, len, ..] -/// lb.ins().lt(); // [i < len, i, len, sum, array] -/// -/// // Finalize, it is at this point that validation will occur -/// lb.build(); -/// ``` -pub struct LoopBuilder<'a> { - dfg: &'a mut DataFlowGraph, - asm: &'a mut InlineAsm, - /// This reference is to the operand stack in the parent [MasmOpBuilder], - /// which represents the operand stack on entry to the loop. Upon finalization - /// of the loop, we use update this operand stack to reflect the state upon - /// exit from the loop. - /// - /// In effect, when the call to `while_true` or `repeat` returns, the operand - /// stack in the parent builder will look as if the loop instruction has finished - /// executing. - in_stack: &'a mut OperandStack, - /// This is the operand stack state within the loop. - /// - /// Upon finalization of the loop instruction, this state is used to validate - /// the effect of the loop body on the operand stack. For `repeat`, which is - /// unconditionally entered, no special validation is performed. However, for - /// `while.true`, we must validate two things: - /// - /// 1. That the top of the stack holds a boolean value - /// 2. That after popping the boolean, the output state of the operand stack - /// matches the input state in number and type of elements. This is required, - /// as otherwise program behavior is undefined based on whether the loop is - /// entered or not. - out_stack: OperandStack, - /// The block to which the loop instruction will be appended - ip: MasmBlockId, - /// The top-level block for the loop - body: MasmBlockId, - /// The type of loop we're building - style: LoopType, -} -impl<'f> LoopBuilder<'f> { - /// Get a builder for a single MASM instruction - pub fn ins<'a, 'b: 'a>(&'b mut self) -> MasmOpBuilder<'a> { - MasmOpBuilder { - dfg: self.dfg, - asm: self.asm, - stack: &mut self.out_stack, - ip: self.body, - } - } - - /// Finalize construction of this loop, performing any final validation. - pub fn build(mut self) { - match self.style { - LoopType::While => { - // First, validate that the top of the stack holds a boolean - let cond = self.out_stack.pop().expect("operand stack is empty"); - assert_eq!(cond, Type::I1, "expected there to be a boolean on top of the stack at the end of the while.true body"); - // Next, validate that the contents of the operand stack match - // the input stack, in order to ensure that the operand stack - // is consistent whether the loop is taken or not - assert_eq!(self.in_stack.stack(), self.out_stack.stack(), "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)); - } - LoopType::Repeat(1) => { - // This is an edge case, but a single iteration `repeat` is no different than - // inlining the loop body into the outer code block and eliding the `repeat`. - // - // Since that is the case, we literally do that transformation here, to simplify - // the IR as much as possible during construction. - let id = self.body; - let mut block = core::mem::replace( - &mut self.asm.blocks[id], - MasmBlock { - id, - ops: smallvec![], - }, - ); - self.asm.blocks[self.ip].append(&mut block.ops); - } - LoopType::Repeat(n) => { - // Apply the stack effects of the loop body `n` times, asserting if some operation - // in the loop fails due to type mismatches. This is sufficient to validate `repeat`, - // as it's iteration count is known statically, entry into the loop is unconditional, - // and the only way to exit the loop is to complete all `n` iterations. - // - // By validating in this way, we also implicitly validate the following: - // - // 1. If we were to translate this to SSA form, the resulting control flow graph would - // have the same number and type of arguments passed to the loop header both on entry - // and along the loopback edges. - // - // 2. If the body of the loop removes elements from the stack, we ensure that all `n` - // iterations can be performed without exhausting the stack, or perform any other invalid - // stack operation. - let code = &self.asm.blocks[self.body]; - for _ in 1..n { - for op in code.ops.iter() { - apply_op_stack_effects(op, &mut self.out_stack, self.dfg, self.asm); - } - } - self.asm.push(self.ip, MasmOp::Repeat(n, self.body)); - } - } - - // Update the operand stack to represent the state after execution of this loop - let in_stack = self.in_stack.stack_mut(); - in_stack.clear(); - in_stack.append(self.out_stack.stack_mut()); - } -} - -/// Asserts that the given value is an integer type which is compatible with u32 operations -macro_rules! assert_compatible_u32_operand { - ($ty:ident) => { - assert!( - $ty.is_pointer() || Type::U32.is_compatible_operand(&$ty), - "expected operand to be u32-compatible, got {}", - $ty - ); - }; - - ($ty:ident, $op:expr) => { - assert!( - $ty.is_pointer() || Type::U32.is_compatible_operand(&$ty), - "expected operand for {} to be u32-compatible, got {}", - $op, - $ty - ); - }; -} - -/// Asserts that the given value is an integer type which is compatible with u32 operations -macro_rules! assert_compatible_u32_operands { - ($lty:ident, $rty:ident) => { - assert_matches!( - $lty, - Type::U8 | Type::U16 | Type::U32 | Type::Ptr(_), - "expected controlling type to be u32-compatible, got {}", - $lty - ); - assert_compatible_operand_types!($lty, $rty); - }; - - ($lty:ident, $rty:ident, $op:expr) => { - assert_matches!( - $lty, - Type::U8 | Type::U16 | Type::U32 | Type::Ptr(_), - "expected controlling type for {} to be u32-compatible, got {}", - $op, - $lty - ); - assert_compatible_operand_types!($lty, $rty, $op); - }; -} - -/// Asserts that the given value is an integer or pointer type which is compatible with basic felt arithmetic -macro_rules! assert_compatible_arithmetic_operand { - ($ty:ident) => { - assert!( - $ty.is_pointer() || Type::Felt.is_compatible_operand(&$ty), - "expected operand to be felt-compatible, got {}", - $ty - ); - }; - - ($ty:ident, $op:expr) => { - assert!( - $ty.is_pointer() || Type::Felt.is_compatible_operand(&$ty), - "expected operand for {} to be felt-compatible, got {}", - $op, - $ty - ); - }; -} - -/// Asserts that the given value is an integer type which is compatible with felt operations -macro_rules! assert_compatible_felt_operand { - ($ty:ident) => { - assert!( - Type::Felt.is_compatible_operand(&$ty), - "expected operand to be felt-compatible, got {}", - $ty - ); - }; - - ($ty:ident, $op:expr) => { - assert!( - Type::Felt.is_compatible_operand(&$ty), - "expected operand for {} to be felt-compatible, got {}", - $op, - $ty - ); - }; -} - -/// Asserts that the given value is an integer or pointer type which is compatible with basic -/// arithmetic operations as a felt -macro_rules! assert_compatible_arithmetic_operands { - ($lty:ident, $rty:ident) => { - assert_matches!( - $lty, - Type::U8 - | Type::I8 - | Type::U16 - | Type::I16 - | Type::U32 - | Type::I32 - | Type::Ptr(_) - | Type::Felt, - "expected controlling type to be felt-compatible, got {}", - $lty - ); - assert_compatible_operand_types!($lty, $rty); - }; - - ($lty:ident, $rty:ident, $op:expr) => { - assert_matches!( - $lty, - Type::U8 - | Type::I8 - | Type::U16 - | Type::I16 - | Type::U32 - | Type::I32 - | Type::Ptr(_) - | Type::Felt, - "expected controlling type for {} to be felt-compatible, got {}", - $op, - $lty - ); - assert_compatible_operand_types!($lty, $rty, $op); - }; -} - -/// Asserts that the given value is an integer type which is compatible with felt operations -macro_rules! assert_compatible_felt_operands { - ($lty:ident, $rty:ident) => { - assert_matches!( - $lty, - Type::U8 | Type::I8 | Type::U16 | Type::I16 | Type::U32 | Type::I32 | Type::Felt, - "expected controlling type to be felt-compatible, got {}", - $lty - ); - assert_compatible_operand_types!($lty, $rty); - }; - - ($lty:ident, $rty:ident, $op:expr) => { - assert_matches!( - $lty, - Type::U8 | Type::I8 | Type::U16 | Type::I16 | Type::U32 | Type::I32 | Type::Felt, - "expected controlling type for {} to be felt-compatible, got {}", - $op, - $lty - ); - assert_compatible_operand_types!($lty, $rty, $op); - }; -} - -/// Asserts that the two operands are of compatible types, where the first operand is assumed to determine the controlling type -macro_rules! assert_compatible_operand_types { - ($lty:ident, $rty:ident) => { - assert!($lty.is_compatible_operand(&$rty), "expected operands to be compatible types, the controlling type {} is not compatible with {}", $lty, $rty); - }; - - ($lty:ident, $rty:ident, $op:expr) => { - assert!($lty.is_compatible_operand(&$rty), "expected operands for {} to be compatible types, the controlling type {} is not compatible with {}", $op, $lty, $rty); - }; -} - -fn apply_op_stack_effects( - op: &MasmOp, - stack: &mut OperandStack, - dfg: &DataFlowGraph, - asm: &InlineAsm, -) { - match op { - MasmOp::Padw => { - stack.padw(); - } - MasmOp::Push(_) => { - stack.push(Type::Felt); - } - MasmOp::Push2(_) => { - stack.push(Type::Felt); - stack.push(Type::Felt); - } - MasmOp::Pushw(_) => { - stack.padw(); - } - MasmOp::PushU8(_) => { - stack.push(Type::U8); - } - MasmOp::PushU16(_) => { - stack.push(Type::U16); - } - MasmOp::PushU32(_) => { - stack.push(Type::U32); - } - MasmOp::Drop => { - stack.drop(); - } - MasmOp::Dropw => { - stack.dropw(); - } - MasmOp::Dup(idx) => { - stack.dup(*idx as usize); - } - MasmOp::Dupw(idx) => { - stack.dupw(*idx as usize); - } - MasmOp::Swap(idx) => { - stack.swap(*idx as usize); - } - MasmOp::Swapw(idx) => { - stack.swapw(*idx as usize); - } - MasmOp::Movup(idx) => { - stack.movup(*idx as usize); - } - MasmOp::Movupw(idx) => { - stack.movupw(*idx as usize); - } - MasmOp::Movdn(idx) => { - stack.movdn(*idx as usize); - } - MasmOp::Movdnw(idx) => { - stack.movdnw(*idx as usize); - } - MasmOp::Cswap | MasmOp::Cswapw => { - let ty = stack.pop().expect("operand stack is empty"); - assert_eq!(ty, Type::I1, "expected boolean operand on top of the stack"); - } - MasmOp::Cdrop => { - let ty = stack.pop().expect("operand stack is empty"); - assert_eq!(ty, Type::I1, "expected boolean operand on top of the stack"); - stack.drop(); - } - MasmOp::Cdropw => { - let ty = stack.pop().expect("operand stack is empty"); - assert_eq!(ty, Type::I1, "expected boolean operand on top of the stack"); - stack.dropw(); - } - MasmOp::Assert - | MasmOp::Assertz - | MasmOp::AssertWithError(_) - | MasmOp::AssertzWithError(_) => { - stack.drop(); - } - MasmOp::AssertEq | MasmOp::AssertEqWithError(_) => { - stack.dropn(2); - } - MasmOp::AssertEqw | MasmOp::AssertEqwWithError(_) => { - stack.dropn(8); - } - MasmOp::LocAddr(_id) | MasmOp::LocStore(_id) | MasmOp::LocStorew(_id) => unreachable!(), - MasmOp::MemLoad | MasmOp::MemLoadOffset => { - let ty = stack.pop().expect("operand stack is empty"); - assert_matches!( - ty, - Type::Ptr(_) | Type::NativePtr(_, _), - "invalid load: expected pointer operand" - ); - stack.push(ty.pointee().unwrap().clone()); - } - MasmOp::MemLoadImm(_) | MasmOp::MemLoadOffsetImm(_, _) => { - // We don't know what we're loading, so fall back to the default type of field element - stack.push(Type::Felt); - } - MasmOp::MemLoadw => { - let ty = stack.pop().expect("operand stack is empty"); - assert_matches!( - ty, - Type::Ptr(_) | Type::NativePtr(_, _), - "invalid load: expected pointer operand" - ); - // We're always loading a raw word with this op - stack.padw(); - } - MasmOp::MemLoadwImm(_) => { - // We're always loading a raw word with this op - stack.padw(); - } - MasmOp::MemStore | MasmOp::MemStoreOffset => { - let ty = stack.pop().expect("operand stack is empty"); - assert_matches!( - ty, - Type::Ptr(_) | Type::NativePtr(_, _), - "invalid store: expected pointer operand" - ); - stack.drop(); - } - MasmOp::MemStoreImm(_) | MasmOp::MemStoreOffsetImm(_, _) => { - stack.drop(); - } - MasmOp::MemStorew => { - let ty = stack.pop().expect("operand stack is empty"); - assert_matches!( - ty, - Type::Ptr(_) | Type::NativePtr(_, _), - "invalid store: expected pointer operand" - ); - stack.dropw(); - } - MasmOp::MemStorewImm(_) => { - stack.dropw(); - } - MasmOp::MemStream => { - // Read two sequential words from memory starting at `a`, overwriting the first two words on the stack, - // and advancing `a` to the next address following the two that were loaded - // [C, B, A, a] <- [*a, *(a + 1), A, a + 2] - assert!( - stack.len() > 12, - "expected at least 13 elements on the stack for mem_stream" - ); - stack.dropw(); - stack.dropw(); - stack.padw(); - stack.padw(); - } - MasmOp::AdvPipe => { - // Pops the next two words from the advice stack, overwrites the - // top of the operand stack with them, and also writes these words - // into memory at `a` and `a + 1` - // - // [C, B, A, a] <- [*a, *(a + 1), A, a + 2] - assert!( - stack.len() > 12, - "expected at least 13 elements on the stack for mem_stream" - ); - stack.dropw(); - stack.dropw(); - stack.padw(); - stack.padw(); - } - MasmOp::AdvPush(n) => { - for _ in 0..(*n) { - stack.push(Type::Felt); - } - } - MasmOp::AdvLoadw => { - assert!( - stack.len() > 3, - "expected at least 4 elements on the stack for mem_stream" - ); - stack.dropw(); - stack.padw(); - } - // This function is not called from [MasmOpBuilder] when building an `if.true` instruction, - // instead, the only time we are evaluating this is when traversing the body of a `repeat.n` - // instruction and applying the stack effects of instructions which have already been inserted - // once. - // - // NOTE: We only apply the effects from a single branch, because we have already validated - // that regardless of which branch is taken, the stack effects are the same. - MasmOp::If(then_body, _else_body) => { - let lty = stack.pop().expect("operand stack is empty"); - assert_eq!(lty, Type::I1, "expected boolean conditional"); - let body = asm.blocks[*then_body].ops.as_slice(); - for op in body.iter() { - apply_op_stack_effects(op, stack, dfg, asm); - } - } - // This function is not called from [MasmOpBuilder] when building an `while.true` instruction, - // instead, the only time we are evaluating this is when traversing the body of a `repeat.n` - // instruction and applying the stack effects of instructions which have already been inserted - // once. - // - // NOTE: We don't need to traverse the body of the `while.true`, because we have already validated - // that whether the loop is taken or not, the stack effects are the same - MasmOp::While(_body) => { - let lty = stack.pop().expect("operand stack is empty"); - assert_eq!(lty, Type::I1, "expected boolean conditional"); - } - // This function is not called from [MasmOpBuilder] when building an `repeat.n` instruction, - // instead, the only time we are evaluating this is when traversing the body of a `repeat.n` - // instruction and applying the stack effects of instructions which have already been inserted - // once. - MasmOp::Repeat(n, body) => { - let body = asm.blocks[*body].ops.as_slice(); - for _ in 0..*n { - for op in body.iter() { - apply_op_stack_effects(op, stack, dfg, asm); - } - } - } - MasmOp::Exec(ref id) => { - execute_call(id, false, stack, dfg); - } - MasmOp::Syscall(ref id) => { - execute_call(id, false, stack, dfg); - } - MasmOp::DynExec | MasmOp::DynCall => { - assert!( - stack.len() > 3, - "expected at least 4 elements on the stack for dynexec/dyncall" - ); - } - MasmOp::ProcRef(_) => { - stack.pushw([Type::Felt, Type::Felt, Type::Felt, Type::Felt]); - } - MasmOp::Add | MasmOp::Sub => { - let rty = stack.pop().expect("operand stack is empty"); - let lty = stack.pop().expect("operand stack is empty"); - assert_compatible_arithmetic_operands!(lty, rty, op); - stack.push(lty); - } - MasmOp::Mul | MasmOp::Div => { - let rty = stack.pop().expect("operand stack is empty"); - let lty = stack.pop().expect("operand stack is empty"); - assert_compatible_felt_operands!(lty, rty, op); - stack.push(lty); - } - MasmOp::AddImm(_) | MasmOp::SubImm(_) | MasmOp::Incr => { - let ty = stack.peek().expect("operand stack is empty"); - assert_compatible_arithmetic_operand!(ty, op); - } - MasmOp::MulImm(_) - | MasmOp::DivImm(_) - | MasmOp::Neg - | MasmOp::Inv - | MasmOp::Pow2 - | MasmOp::ExpImm(_) => { - let ty = stack.peek().expect("operand stack is empty"); - assert_compatible_felt_operand!(ty, op); - } - MasmOp::Exp => { - let rty = stack.pop().expect("operand stack is empty"); - let lty = stack.pop().expect("operand stack is empty"); - assert_compatible_felt_operands!(lty, rty); - stack.push(lty); - } - MasmOp::Not | MasmOp::AndImm(_) | MasmOp::OrImm(_) | MasmOp::XorImm(_) => { - let ty = stack.pop().expect("operand stack is empty"); - assert_eq!(ty, Type::I1, "expected boolean type"); - stack.push(ty); - } - MasmOp::And | MasmOp::Or | MasmOp::Xor => { - let rty = stack.pop().expect("operand stack is empty"); - let lty = stack.pop().expect("operand stack is empty"); - assert_eq!(lty, rty, "expected operands for {} to be the same type", op); - assert_eq!(lty, Type::I1, "expected boolean operands for {}", op); - stack.push(lty); - } - MasmOp::Eq | MasmOp::Neq | MasmOp::Gt | MasmOp::Gte | MasmOp::Lt | MasmOp::Lte => { - let rty = stack.pop().expect("operand stack is empty"); - let lty = stack.pop().expect("operand stack is empty"); - assert_compatible_felt_operands!(lty, rty, op); - stack.push(Type::I1); - } - MasmOp::EqImm(_) - | MasmOp::NeqImm(_) - | MasmOp::GtImm(_) - | MasmOp::GteImm(_) - | MasmOp::LtImm(_) - | MasmOp::LteImm(_) - | MasmOp::IsOdd => { - let ty = stack.pop().expect("operand stack is empty"); - assert_compatible_felt_operand!(ty, op); - stack.push(Type::I1); - } - MasmOp::Eqw => { - stack.dropn(8); - stack.push(Type::I1); - } - MasmOp::Clk => { - stack.push(Type::Felt); - } - MasmOp::Caller => { - assert!( - stack.len() > 3, - "expected at least 4 elements on the operand stack" - ); - stack.popw(); - stack.pushw([Type::Felt, Type::Felt, Type::Felt, Type::Felt]); - } - MasmOp::U32Test => { - assert!(!stack.is_empty()); - stack.push(Type::I1); - } - MasmOp::U32Testw => { - assert!( - stack.len() > 3, - "expected at least 4 elements on the operand stack" - ); - stack.push(Type::I1); - } - MasmOp::U32Assert | MasmOp::U32AssertWithError(_) => { - assert!(!stack.is_empty()); - } - MasmOp::U32Assert2 | MasmOp::U32Assert2WithError(_) => { - assert!( - stack.len() > 1, - "expected at least 2 elements on the operand stack" - ); - } - MasmOp::U32Assertw | MasmOp::U32AssertwWithError(_) => { - assert!( - stack.len() > 3, - "expected at least 4 elements on the operand stack" - ); - } - MasmOp::U32Cast => { - let lty = stack.pop().expect("operand stack is empty"); - assert_eq!(lty, Type::Felt, "expected felt operand"); - stack.push(Type::U32); - } - MasmOp::U32Split => { - let lty = stack.pop().expect("operand stack is empty"); - assert_eq!(lty, Type::Felt, "expected felt operand"); - stack.push(Type::U32); - stack.push(Type::U32); - } - MasmOp::U32Lt | MasmOp::U32Gt | MasmOp::U32Lte | MasmOp::U32Gte => { - let rty = stack.pop().expect("failed to pop right operand: stack is empty"); - let lty = stack.pop().expect("failed to pop left operand: stack is empty"); - assert_compatible_u32_operands!(lty, rty, op); - stack.push(Type::I1); - } - MasmOp::U32Div - | MasmOp::U32Mod - | MasmOp::U32DivMod - | MasmOp::U32Shl - | MasmOp::U32Shr - | MasmOp::U32Rotl - | MasmOp::U32Rotr - | MasmOp::U32Min - | MasmOp::U32Max - | MasmOp::U32WrappingAdd - | MasmOp::U32WrappingSub - | MasmOp::U32WrappingMul - | MasmOp::U32And - | MasmOp::U32Or - | MasmOp::U32Xor => { - let rty = stack.pop().expect("operand stack is empty"); - let lty = stack.pop().expect("operand stack is empty"); - assert_compatible_u32_operands!(lty, rty, op); - stack.push(lty); - } - MasmOp::U32DivImm(_) - | MasmOp::U32ModImm(_) - | MasmOp::U32DivModImm(_) - | MasmOp::U32ShlImm(_) - | MasmOp::U32ShrImm(_) - | MasmOp::U32RotlImm(_) - | MasmOp::U32RotrImm(_) - | MasmOp::U32Popcnt - | MasmOp::U32WrappingAddImm(_) - | MasmOp::U32WrappingSubImm(_) - | MasmOp::U32WrappingMulImm(_) - | MasmOp::U32Not => { - let ty = stack.pop().expect("operand stack is empty"); - assert_compatible_u32_operand!(ty, op); - stack.push(ty); - } - MasmOp::U32OverflowingAdd | MasmOp::U32OverflowingSub | MasmOp::U32OverflowingMul => { - let rty = stack.pop().expect("operand stack is empty"); - let lty = stack.pop().expect("operand stack is empty"); - assert_compatible_u32_operands!(lty, rty, op); - stack.push(lty); - stack.push(Type::I1); - } - MasmOp::U32OverflowingAddImm(_) - | MasmOp::U32OverflowingSubImm(_) - | MasmOp::U32OverflowingMulImm(_) => { - let ty = stack.pop().expect("operand stack is empty"); - assert_compatible_u32_operand!(ty, op); - stack.push(ty); - stack.push(Type::I1); - } - MasmOp::U32OverflowingAdd3 => { - let cty = stack.pop().expect("operand stack is empty"); - let bty = stack.pop().expect("operand stack is empty"); - let aty = stack.pop().expect("operand stack is empty"); - assert_compatible_u32_operands!(aty, bty); - assert_compatible_u32_operands!(aty, cty); - stack.push(aty); - stack.push(Type::U32); - } - MasmOp::U32OverflowingMadd => { - let bty = stack.pop().expect("operand stack is empty"); - let aty = stack.pop().expect("operand stack is empty"); - let cty = stack.pop().expect("operand stack is empty"); - assert_compatible_u32_operands!(aty, bty); - assert_compatible_u32_operands!(aty, cty); - stack.push(aty); - stack.push(Type::U32); - } - MasmOp::U32WrappingAdd3 => { - let cty = stack.pop().expect("operand stack is empty"); - let bty = stack.pop().expect("operand stack is empty"); - let aty = stack.pop().expect("operand stack is empty"); - assert_compatible_u32_operands!(aty, bty); - assert_compatible_u32_operands!(aty, cty); - stack.push(aty); - } - MasmOp::U32WrappingMadd => { - let bty = stack.pop().expect("operand stack is empty"); - let aty = stack.pop().expect("operand stack is empty"); - let cty = stack.pop().expect("operand stack is empty"); - assert_compatible_u32_operands!(aty, bty); - assert_compatible_u32_operands!(aty, cty); - stack.push(aty); - } - } -} - -/// Validate that a call to `id` is possible given the current state of the operand stack, -/// and if so, update the state of the operand stack to reflect the call. -fn execute_call( - id: &FunctionIdent, - is_syscall: bool, - stack: &mut OperandStack, - dfg: &DataFlowGraph, -) { - let import = dfg - .get_import(id) - .expect("unknown function, are you missing an import?"); - if is_syscall { - assert_eq!( - import.signature.cc, - CallConv::Kernel, - "cannot call a non-kernel function with the `syscall` instruction" - ); - } else { - assert_ne!( - import.signature.cc, - CallConv::Kernel, - "`syscall` cannot be used to call non-kernel functions" - ); - } - match import.signature.cc { - // For now, we're treating all calling conventions the same as SystemV - CallConv::Fast | CallConv::SystemV | CallConv::Kernel => { - // Visit the argument list in reverse (so that the top of the stack on entry - // is the first argument), and allocate elements based on the argument types. - let mut elements_needed = 0; - for param in import.signature.params().iter().rev() { - elements_needed += param.ty.size_in_felts(); - } - - // Verify that we have `elements_needed` values on the operand stack - let elements_available = stack.len(); - assert!(elements_needed <= elements_available, "the operand stack does not contain enough values to call {} ({} exepected vs {} available)", id, elements_needed, elements_available); - stack.dropn(elements_needed); - - // Update the operand stack to reflect the results - for result in import.signature.results().iter().rev() { - push_type_on_stack(result.ty.clone(), stack); - } - } - } -} - -fn push_type_on_stack(ty: Type, stack: &mut OperandStack) { - let parts = ty - .to_raw_parts() - .expect("invalid unknown type: cannot proceed"); - for part in parts.into_iter().rev() { - stack.push(part); - } -} diff --git a/hir/src/asm/display.rs b/hir/src/asm/display.rs deleted file mode 100644 index 88e8fc823..000000000 --- a/hir/src/asm/display.rs +++ /dev/null @@ -1,266 +0,0 @@ -use std::fmt::{self, Write}; - -use cranelift_entity::PrimaryMap; - -use super::*; -use crate::{write::DisplayIndent, DataFlowGraph, FunctionIdent, Ident, Symbol}; - -pub struct DisplayInlineAsm<'a> { - function: Option, - asm: &'a InlineAsm, - dfg: &'a DataFlowGraph, - indent: usize, -} -impl<'a> DisplayInlineAsm<'a> { - pub fn new( - function: Option, - asm: &'a InlineAsm, - dfg: &'a DataFlowGraph, - indent: usize, - ) -> Self { - Self { - function, - asm, - dfg, - indent, - } - } -} -impl<'a> fmt::Display for DisplayInlineAsm<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use crate::display::DisplayValues; - - { - let args = self.asm.args.as_slice(&self.dfg.value_lists); - writeln!(f, "({}) {{", DisplayValues::new(args.iter()))?; - } - - let indent = self.indent; - let block = self.asm.body; - writeln!( - f, - "{}", - DisplayMasmBlock { - function: self.function, - imports: None, - blocks: &self.asm.blocks, - block, - indent: indent + 1, - } - )?; - - writeln!(f, "{}}}", DisplayIndent(indent)) - } -} - -pub struct DisplayMasmBlock<'a> { - function: Option, - imports: Option<&'a ModuleImportInfo>, - blocks: &'a PrimaryMap, - block: MasmBlockId, - indent: usize, -} -impl<'a> DisplayMasmBlock<'a> { - pub fn new( - function: Option, - imports: Option<&'a ModuleImportInfo>, - blocks: &'a PrimaryMap, - block: MasmBlockId, - indent: usize, - ) -> Self { - Self { - function, - imports, - blocks, - block, - indent, - } - } -} -impl<'a> fmt::Display for DisplayMasmBlock<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let block = &self.blocks[self.block]; - let indent = self.indent; - for (i, op) in block.ops.iter().enumerate() { - if i > 0 { - f.write_char('\n')?; - } - write!( - f, - "{}", - DisplayOp { - function: self.function, - imports: self.imports, - blocks: self.blocks, - op, - indent - } - )?; - } - Ok(()) - } -} - -struct DisplayOp<'a> { - function: Option, - imports: Option<&'a ModuleImportInfo>, - blocks: &'a PrimaryMap, - op: &'a MasmOp, - indent: usize, -} -impl<'a> DisplayOp<'a> { - #[inline(always)] - pub fn is_local_module(&self, id: &Ident) -> bool { - match self.function { - Some(function) => &function.module == id, - None => self - .imports - .map(|imports| !imports.is_import(id)) - .unwrap_or(false), - } - } - - pub fn get_module_alias(&self, module: Ident) -> Symbol { - self.imports - .and_then(|imports| imports.alias(&module)) - .unwrap_or(module) - .as_symbol() - } -} -impl<'a> fmt::Display for DisplayOp<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", DisplayIndent(self.indent))?; - match self.op { - MasmOp::Push(imm) => write!(f, "push.{imm}"), - MasmOp::Push2([a, b]) => write!(f, "push.{a}.{b}"), - MasmOp::Pushw(word) => write!( - f, - "push.{}.{}.{}.{}", - &word[0], &word[1], &word[2], &word[3] - ), - MasmOp::PushU8(imm) => write!(f, "push.{imm}"), - MasmOp::PushU16(imm) => write!(f, "push.{imm}"), - MasmOp::PushU32(imm) => write!(f, "push.{imm}"), - op @ (MasmOp::Dup(idx) - | MasmOp::Dupw(idx) - | MasmOp::Swap(idx) - | MasmOp::Swapw(idx) - | MasmOp::Movup(idx) - | MasmOp::Movupw(idx) - | MasmOp::Movdn(idx) - | MasmOp::Movdnw(idx)) => write!(f, "{op}.{idx}"), - op @ (MasmOp::LocAddr(id) | MasmOp::LocStore(id) | MasmOp::LocStorew(id)) => { - write!(f, "{op}.{}", id.as_usize()) - } - op @ (MasmOp::MemLoadImm(addr) - | MasmOp::MemLoadwImm(addr) - | MasmOp::MemStoreImm(addr) - | MasmOp::MemStorewImm(addr)) => write!(f, "{op}.{}", Address(*addr)), - op @ (MasmOp::MemLoadOffsetImm(addr, offset) - | MasmOp::MemStoreOffsetImm(addr, offset)) => { - write!(f, "{op}.{}.{offset}", Address(*addr)) - } - op @ MasmOp::AdvPush(n) => { - write!(f, "{op}.{n}") - } - MasmOp::If(then_blk, else_blk) => { - write!( - f, - "if.true\n{}\n{}else\n{}\n{}end", - DisplayMasmBlock { - function: self.function, - imports: self.imports, - blocks: self.blocks, - block: *then_blk, - indent: self.indent + 1 - }, - DisplayIndent(self.indent), - DisplayMasmBlock { - function: self.function, - imports: self.imports, - blocks: self.blocks, - block: *else_blk, - indent: self.indent + 1 - }, - DisplayIndent(self.indent), - ) - } - MasmOp::While(blk) => { - write!( - f, - "while.true\n{}\n{}end", - DisplayMasmBlock { - function: self.function, - imports: self.imports, - blocks: self.blocks, - block: *blk, - indent: self.indent + 1 - }, - DisplayIndent(self.indent), - ) - } - MasmOp::Repeat(n, blk) => { - write!( - f, - "repeat.{n}\n{}\n{}end", - DisplayMasmBlock { - function: self.function, - imports: self.imports, - blocks: self.blocks, - block: *blk, - indent: self.indent + 1 - }, - DisplayIndent(self.indent), - ) - } - op @ (MasmOp::Exec(id) | MasmOp::Syscall(id) | MasmOp::ProcRef(id)) => { - let FunctionIdent { module, function } = id; - if self.is_local_module(module) { - write!(f, "{op}.{function}") - } else { - let alias = self.get_module_alias(*module); - write!(f, "{op}.{alias}::{function}") - } - } - op @ (MasmOp::AndImm(imm) | MasmOp::OrImm(imm) | MasmOp::XorImm(imm)) => { - write!(f, "{op}.{imm}") - } - op @ MasmOp::ExpImm(imm) => write!(f, "{op}.{imm}"), - op @ (MasmOp::AddImm(imm) - | MasmOp::SubImm(imm) - | MasmOp::MulImm(imm) - | MasmOp::DivImm(imm) - | MasmOp::EqImm(imm) - | MasmOp::NeqImm(imm) - | MasmOp::GtImm(imm) - | MasmOp::GteImm(imm) - | MasmOp::LtImm(imm) - | MasmOp::LteImm(imm)) => write!(f, "{op}.{imm}"), - op @ (MasmOp::U32OverflowingAddImm(imm) - | MasmOp::U32WrappingAddImm(imm) - | MasmOp::U32OverflowingSubImm(imm) - | MasmOp::U32WrappingSubImm(imm) - | MasmOp::U32OverflowingMulImm(imm) - | MasmOp::U32WrappingMulImm(imm) - | MasmOp::U32DivImm(imm) - | MasmOp::U32ModImm(imm) - | MasmOp::U32DivModImm(imm) - | MasmOp::U32ShlImm(imm) - | MasmOp::U32ShrImm(imm) - | MasmOp::U32RotlImm(imm) - | MasmOp::U32RotrImm(imm)) => write!(f, "{op}.{imm}"), - op => write!(f, "{op}"), - } - } -} - -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 deleted file mode 100644 index c9f850522..000000000 --- a/hir/src/asm/import.rs +++ /dev/null @@ -1,271 +0,0 @@ -use core::{ - fmt::Write, - hash::{Hash, Hasher}, - str::FromStr, -}; - -use anyhow::bail; -use miden_diagnostics::{SourceSpan, Spanned}; -use rustc_hash::{FxHashMap, FxHashSet}; - -use crate::{FunctionIdent, Ident, Symbol}; - -#[derive(Default)] -pub struct ModuleImportInfo { - /// This maps original, fully-qualified module names to their corresponding import - modules: FxHashMap, - /// This maps known aliases to their fully-qualified identifiers - aliases: FxHashMap, - /// This maps short-form/aliased module names to the functions imported from that module - functions: FxHashMap>, -} -impl ModuleImportInfo { - /// Inserts a new import in the table - pub fn insert(&mut self, import: MasmImport) { - let name = Ident::new(import.name, import.span); - assert!(self.modules.insert(name, import).is_none()); - } - - /// Adds an import of the given function to the import table - /// - /// NOTE: It is assumed that the caller is adding imports using fully-qualified names. - pub fn add(&mut self, id: FunctionIdent) { - use std::collections::hash_map::Entry; - - let module_id = id.module; - match self.modules.entry(module_id) { - Entry::Vacant(entry) => { - let alias = match module_id.as_str().rsplit_once("::") { - None => module_id.as_symbol(), - Some((_, alias)) => Symbol::intern(alias), - }; - let span = module_id.span(); - let alias_id = if self.aliases.contains_key(&alias) { - // The alias is already used by another module, we must - // produce a new, unique alias to avoid conflicts. We - // use the hash of the fully-qualified name for this - // purpose, hex-encoded - let mut hasher = rustc_hash::FxHasher::default(); - alias.as_str().hash(&mut hasher); - let mut buf = String::with_capacity(16); - write!(&mut buf, "{:x}", hasher.finish()).expect("failed to write string"); - let alias = Symbol::intern(buf.as_str()); - let alias_id = Ident::new(alias, span); - assert_eq!( - self.aliases.insert(alias_id, module_id), - None, - "unexpected aliasing conflict" - ); - alias_id - } else { - Ident::new(alias, span) - }; - entry.insert(MasmImport { - span, - name: module_id.as_symbol(), - alias: alias_id.name, - }); - self.aliases.insert(alias_id, module_id); - self.functions.entry(alias_id).or_default().insert(id); - } - Entry::Occupied(_) => { - let alias = self.aliases[&module_id]; - let functions = self.functions.entry(alias).or_default(); - functions.insert(id); - } - } - } - - /// Returns true if there are no imports recorded - pub fn is_empty(&self) -> bool { - self.modules.is_empty() - } - - /// Given a fully-qualified module name, look up the corresponding import metadata - pub fn get(&self, module: &Q) -> Option<&MasmImport> - where - Ident: core::borrow::Borrow, - Q: Hash + Eq + ?Sized, - { - self.modules.get(module) - } - - /// Given a fully-qualified module name, get the aliased identifier - pub fn alias(&self, module: &Q) -> Option - where - Ident: core::borrow::Borrow, - Q: Hash + Eq + ?Sized, - { - self.modules - .get(module) - .map(|i| Ident::new(i.alias, i.span)) - } - - /// Given an aliased module name, get the fully-qualified identifier - pub fn unalias(&self, alias: &Q) -> Option - where - Ident: core::borrow::Borrow, - Q: Hash + Eq + ?Sized, - { - self.aliases.get(alias).copied() - } - - /// Returns true if `module` is an imported module - pub fn is_import(&self, module: &Q) -> bool - where - Ident: core::borrow::Borrow, - Q: Hash + Eq + ?Sized, - { - self.modules.contains_key(module) - } - - /// Given a module alias, get the set of functions imported from that module - pub fn imported(&self, alias: &Q) -> Option<&FxHashSet> - where - Ident: core::borrow::Borrow, - Q: Hash + Eq + ?Sized, - { - self.functions.get(alias) - } - - /// Get an iterator over the [MasmImport] records in this table - pub fn iter(&self) -> impl Iterator { - self.modules.values() - } -} - -/// This represents an import statement in Miden Assembly -#[derive(Debug, Copy, Clone, Spanned)] -pub struct MasmImport { - /// 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 MasmImport { - /// 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 MasmImport {} -impl PartialEq for MasmImport { - 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 MasmImport { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for MasmImport { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.name - .cmp(&other.name) - .then_with(|| self.alias.cmp(&other.alias)) - } -} -impl Hash for MasmImport { - fn hash(&self, state: &mut H) { - self.name.hash(state); - self.alias.hash(state); - } -} -impl TryFrom for MasmImport { - type Error = anyhow::Error; - - fn try_from(module: Ident) -> Result { - let name = module.as_str(); - if name.contains(char::is_whitespace) { - bail!("invalid module identifier '{name}': cannot contain whitespace",); - } - match name.rsplit_once("::") { - None => { - let name = module.as_symbol(); - Ok(Self { - span: module.span(), - name, - alias: name, - }) - } - Some((_, alias)) if alias.is_empty() => { - bail!("invalid module identifier '{name}': trailing '::' is invalid"); - } - Some((_, alias)) => { - let name = module.as_symbol(); - let alias = Symbol::intern(alias); - Ok(Self { - span: module.span(), - name, - alias, - }) - } - } - } -} -impl FromStr for MasmImport { - type Err = anyhow::Error; - - /// Parse an import statement as seen in Miden Assembly, e.g. `use std::math::u64->bigint` - fn from_str(s: &str) -> Result { - let s = s.trim(); - let s = s.strip_prefix("use ").unwrap_or(s); - match s.rsplit_once("->") { - None => { - let name = Ident::with_empty_span(Symbol::intern(s)); - name.try_into() - } - Some((_, alias)) if alias.is_empty() => { - bail!("invalid import '{s}': alias cannot be empty") - } - Some((fqn, alias)) => { - let name = Symbol::intern(fqn); - let alias = Symbol::intern(alias); - Ok(Self { - span: SourceSpan::UNKNOWN, - name, - alias, - }) - } - } - } -} diff --git a/hir/src/asm/isa.rs b/hir/src/asm/isa.rs deleted file mode 100644 index 6632f5b73..000000000 --- a/hir/src/asm/isa.rs +++ /dev/null @@ -1,1342 +0,0 @@ -use std::fmt; - -use cranelift_entity::entity_impl; -use rustc_hash::FxHashMap; -use smallvec::{smallvec, SmallVec}; - -use crate::{Felt, FunctionIdent, Ident, LocalId}; - -/// A handle that refers to a MASM code block -#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct MasmBlockId(u32); -entity_impl!(MasmBlockId, "blk"); - -/// Represents a single code block in Miden Assembly -#[derive(Debug, Clone, PartialEq)] -pub struct MasmBlock { - pub id: MasmBlockId, - pub ops: SmallVec<[MasmOp; 4]>, -} -impl MasmBlock { - /// Returns true if there are no instructions in this block - #[inline(always)] - pub fn is_empty(&self) -> bool { - self.ops.is_empty() - } - - /// Returns the instructions contained in this block as a slice - #[inline(always)] - pub fn ops(&self) -> &[MasmOp] { - self.ops.as_slice() - } - - /// Appends `op` to this code block - #[inline(always)] - pub fn push(&mut self, op: MasmOp) { - self.ops.push(op); - } - - /// Append `n` copies of `op` to the current block - #[inline] - pub fn push_n(&mut self, count: usize, op: MasmOp) { - for _ in 0..count { - self.ops.push(op); - } - } - - /// Append `n` copies of the sequence `ops` to this block - #[inline] - pub fn push_repeat(&mut self, ops: &[MasmOp], count: usize) { - for _ in 0..count { - self.ops.extend_from_slice(ops); - } - } - - /// Append `n` copies of the sequence `ops` to this block - #[inline] - pub fn push_template(&mut self, count: usize, template: F) - where - F: Fn(usize) -> [MasmOp; N], - { - for n in 0..count { - self.ops.extend_from_slice(&template(n)); - } - } - - /// Appends instructions from `slice` to the end of this block - #[inline] - pub fn extend_from_slice(&mut self, slice: &[MasmOp]) { - 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) { - self.ops.extend(ops); - } - - /// Appends instructions from `other` to the end of this block - #[inline] - pub fn append(&mut self, other: &mut SmallVec) - where - B: smallvec::Array, - { - self.ops.append(other); - } -} - -/// This enum represents the Miden Assembly (MASM) instruction set. -/// -/// Not all MASM instructions are necessarily represented here, only those we -/// actually use, or intend to use, when compiling from Miden IR. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum MasmOp { - /// Pushes a null word on the stack, i.e. four 0 values - Padw, - /// Pushes the given field element constant on top of the stack - Push(Felt), - /// Pushes a pair of field elements on top of the stack - Push2([Felt; 2]), - /// Pushes the given word constant on top of the stack - Pushw([Felt; 4]), - /// Pushes the given 8-bit constant on top of the stack - PushU8(u8), - /// Pushes the given 16-bit constant on top of the stack - PushU16(u16), - /// Pushes the given 32-bit constant on top of the stack - PushU32(u32), - /// Removes the item on the top of the stack - Drop, - /// Removes the top 4 items on the stack - Dropw, - /// Copies the `n`th item on the stack to the top of stack - /// - /// * `Dup(0)` duplicates the item on top of the stack - Dup(u8), - /// Copies the `n`th word on the stack, to the top of the stack - /// - /// The only values of `n` which are valid, are 0, 1, 2, 3; or - /// in other words, the 4 words which make up the top 16 elements - /// of the stack. - Dupw(u8), - /// Swaps the 1st and `n`th items on the stack - /// - /// * `Swap(1)` swaps the top two elements of the stack - Swap(u8), - /// Swaps the 1st and `n`th words on the stack - /// - /// The only values of `n` which are valid, are 1, 2, 3; or - /// in other words, the 3 words which make up the last 12 elements - /// of the stack. - Swapw(u8), - /// Moves the `n`th stack item to top of stack - /// - /// * `Movup(1)` is equivalent to `Swap(1)` - Movup(u8), - /// Moves the `n`th stack word to the top of the stack - /// - /// The only values of `n` which are valid are 2 and 3. Use `Swapw(1)` - /// if you want to move the second word to the top. - Movupw(u8), - /// Moves the top of stack to the `n`th index of the stack - /// - /// * `Movdn(1)` is equivalent to `Swap(1)` - Movdn(u8), - /// Moves the top word of the stack, into position as the `n`th word on the stack. - /// - /// The only values of `n` which are valid are 2 and 3. Use `Swapw(1)` - /// if you want to make the top word the second word. - Movdnw(u8), - /// Pops `c, b, a` off the stack, and swaps `b` and `a` if `c` is 1, or leaves - /// them as-is when 0. - /// - /// Traps if `c` is > 1. - Cswap, - /// Pops `c, B, A` off the stack, where `B` and `A` are words, and swaps `B` and `A` - /// if `c` is 1, or leaves them as-is when 0. - /// - /// Traps if `c` is > 1. - Cswapw, - /// Pops `c, b, a` off the stack, and pushes back `b` if `c` is 1, and `a` if 0. - /// - /// Traps if `c` is > 1. - Cdrop, - /// Pops `c, B, A` off the stack, where `B` and `A` are words, and pushes back `B` - /// if `c` is 1, and `A` if 0. - /// - /// Traps if `c` is > 1. - Cdropw, - /// Pops a value off the stack and asserts that it is equal to 1 - Assert, - /// Pops a value off the stack and asserts that it is equal to 1, raising the given error code - AssertWithError(u32), - /// Pops a value off the stack and asserts that it is equal to 0 - Assertz, - /// Pops a value off the stack and asserts that it is equal to 0, raising the given error code - AssertzWithError(u32), - /// Pops two values off the stack and asserts that they are equal - AssertEq, - /// Pops two values off the stack and asserts that they are equal, raising the given error code - AssertEqWithError(u32), - /// Pops two words off the stack and asserts that they are equal - AssertEqw, - /// Pops two words off the stack and asserts that they are equal, raising the given error code - AssertEqwWithError(u32), - /// Places the memory address of the given local index on top of the stack - LocAddr(LocalId), - /// Writes a value to the first element of the word at the address corresponding to the given local index - LocStore(LocalId), - /// Writes a word to the address corresponding to the given local index - LocStorew(LocalId), - /// Pops `a`, representing a memory address, from the top of the stack, then loads the - /// first element of the word starting at that address, placing it on top of the stack. - /// - /// Traps if `a` >= 2^32 - MemLoad, - /// Same as above, but the address is given as an immediate - MemLoadImm(u32), - /// Pops `a`, representing a memory address + offset pair, from the top of the stack, then loads the - /// element at the given offset from the base of the word starting at that address, placing it on top - /// of the stack. - /// - /// Traps if `a` >= 2^32 - /// - /// NOTE: This instruction doesn't actually exist in Miden Assembly yet, it is a proposed extension of - /// `MemLoad` which allows addressing all field elements of a word individually. It is here for testing. - MemLoadOffset, - /// Same as above, but the address and offset are given as a immediates - MemLoadOffsetImm(u32, u8), - /// Pops `a`, representing a memory address, from the top of the stack, then overwrites - /// the top word of the stack with the word starting at that address. - /// - /// Traps if `a` >= 2^32 - MemLoadw, - /// Same as above, but the address is given as an immediate - MemLoadwImm(u32), - /// Pops `a, v` from the stack, where `a` represents a memory address, and `v` the value - /// to be stored, and stores `v` as the element as the first element of the word starting - /// at that address. The remaining elements of the word are not modified. - /// - /// Traps if `a` >= 2^32 - MemStore, - /// Same as above, but the address is given as an immediate - MemStoreImm(u32), - /// Pops `a, v` from the stack, where `a` represents a memory address + offset pair, and `v` the value - /// to be stored, and stores `v` as the element at the given offset from the base of the word starting - /// at that address. The remaining elements of the word are not modified. - /// - /// Traps if `a` >= 2^32 - /// - /// NOTE: This instruction doesn't actually exist in Miden Assembly yet, it is a proposed extension of - /// `MemStore` which allows addressing all field elements of a word individually. It is here for testing. - MemStoreOffset, - /// Same as above, but the address and offset are given as a immediates - MemStoreOffsetImm(u32, u8), - /// Pops `a, V` from the stack, where `a` represents a memory address, and `V` is a word to be stored - /// at that location, and overwrites the word located at `a`. - /// - /// Traps if `a` >= 2^32 - MemStorew, - /// Same as above, but the address is given as an immediate - MemStorewImm(u32), - /// Read two sequential words from memory starting at `a`, overwriting the first two words on the stack, - /// and advancing `a` to the next address following the two that were loaded - /// [C, B, A, a] <- [*a, *(a + 1), A, a + 2] - MemStream, - /// Pops the next two words from the advice stack, overwrites the - /// top of the operand stack with them, and also writes these words - /// into memory at `a` and `a + 1` - /// - /// [C, B, A, a] <- [*a, *(a + 1), A, a + 2] - AdvPipe, - /// TODO - AdvPush(u8), - /// TODO - AdvLoadw, - /// Pops the top of the stack, and evaluates the ops in - /// the block of code corresponding to the branch taken. - /// - /// If the value is `1`, corresponding to `true`, the first block - /// is evaluated. Otherwise, the value must be `0`, corresponding to - /// `false`, and the second block is evaluated. - If(MasmBlockId, MasmBlockId), - /// Pops the top of the stack, and evaluates the given block of - /// code if the value is `1`, corresponding to `true`. - /// - /// Otherwise, the value must be `0`, corresponding to `false`, - /// and the block is skipped. - While(MasmBlockId), - /// Repeatedly evaluates the given block, `n` times. - Repeat(u8, MasmBlockId), - /// Pops `N` args off the stack, executes the procedure, results will be placed on the stack - Exec(FunctionIdent), - /// Pops `N` args off the stack, executes the procedure in the root context, results will be placed on the stack - Syscall(FunctionIdent), - /// Pops the address (MAST root hash) of a callee off the stack, and dynamically `exec` the function - DynExec, - /// TODO - DynCall, - /// Pushes the address (MAST root hash) of the given function on the stack, to be used by `dynexec` or `dyncall` - ProcRef(FunctionIdent), - /// Pops `b, a` off the stack, and places the result of `(a + b) mod p` on the stack - Add, - /// Same as above, but the immediate is used for `b` - AddImm(Felt), - /// Pops `b, a` off the stack, and places the result of `(a - b) mod p` on the stack - Sub, - /// Same as above, but the immediate is used for `b` - SubImm(Felt), - /// Pops `b, a` off the stack, and places the result of `(a * b) mod p` on the stack - Mul, - /// Same as above, but the immediate is used for `b` - MulImm(Felt), - /// Pops `b, a` off the stack, and places the result of `(a * b^-1) mod p` on the stack - /// - /// NOTE: `b` must not be 0 - Div, - /// Same as above, but the immediate is used for `b` - DivImm(Felt), - /// Pops `a` off the stack, and places the result of `-a mod p` on the stack - Neg, - /// Pops `a` off the stack, and places the result of `a^-1 mod p` on the stack - /// - /// NOTE: `a` must not be equal to 0 - Inv, - /// Pops `a` off the stack, and places the result of incrementing it by 1 back on the stack - Incr, - /// Pops `a` off the stack, and places the result of `2^a` on the stack - /// - /// NOTE: `a` must not be > 63 - Pow2, - /// Pops `a` and `b` off the stack, and places the result of `a^b` on the stack - /// - /// NOTE: `b` must not be > 63 - Exp, - /// Pops `a` off the stack, and places the result of `a^` on the stack - /// - /// NOTE: `imm` must not be > 63 - ExpImm(u8), - /// Pops `a` off the stack, and places the result of `1 - a` on the stack - /// - /// NOTE: `a` must be boolean - Not, - /// Pops `b, a` off the stack, and places the result of `a * b` on the stack - /// - /// NOTE: `a` must be boolean - And, - /// Same as above, but `a` is taken from the stack, and `b` is the immediate. - /// - /// NOTE: `a` must be boolean - AndImm(bool), - /// Pops `b, a` off the stack, and places the result of `a + b - a * b` on the stack - /// - /// NOTE: `a` must be boolean - Or, - /// Same as above, but `a` is taken from the stack, and `b` is the immediate. - /// - /// NOTE: `a` must be boolean - OrImm(bool), - /// Pops `b, a` off the stack, and places the result of `a + b - 2 * a * b` on the stack - /// - /// NOTE: `a` and `b` must be boolean - Xor, - /// Same as above, but `a` is taken from the stack, and `b` is the immediate. - /// - /// NOTE: `a` must be boolean - XorImm(bool), - /// Pops `b, a` off the stack, and places the result of `a == b` on the stack - Eq, - /// Same as above, but `b` is provided by the immediate - EqImm(Felt), - /// Pops `b, a` off the stack, and places the result of `a != b` on the stack - Neq, - /// Same as above, but `b` is provided by the immediate - NeqImm(Felt), - /// Pops `b, a` off the stack, and places the result of `a > b` on the stack - Gt, - /// Same as above, but `b` is provided by the immediate - GtImm(Felt), - /// Pops `b, a` off the stack, and places the result of `a >= b` on the stack - Gte, - /// Same as above, but `b` is provided by the immediate - GteImm(Felt), - /// Pops `b, a` off the stack, and places the result of `a < b` on the stack - Lt, - /// Same as above, but `b` is provided by the immediate - LtImm(Felt), - /// Pops `b, a` off the stack, and places the result of `a <= b` on the stack - Lte, - /// Same as above, but `b` is provided by the immediate - LteImm(Felt), - /// Pops `a` off the stack, and places the 1 on the stack if `a` is odd, else 0 - IsOdd, - /// Pops `B, A` off the stack, and places the result of `A == B` on the stack, - /// where the uppercase variables here represent words, rather than field elements. - /// - /// The comparison works by comparing pairs of elements from each word - Eqw, - /// When called via `syscall`, this pushes the hash of the caller's MAST root on the stack - Caller, - /// Pushes the current value of the cycle counter (clock) on the stack - Clk, - /// Peeks `a` from the top of the stack, and places the 1 on the stack if `a < 2^32`, else 0 - U32Test, - /// Peeks `A` from the top of the stack, and places the 1 on the stack if `forall a : A, a < 2^32`, else 0 - U32Testw, - /// Peeks `a` from the top of the stack, and traps if `a >= 2^32` - U32Assert, - /// Peeks `a` from the top of the stack, and traps if `a >= 2^32`, raising the given error code - U32AssertWithError(u32), - /// Peeks `b, a` from the top of the stack, and traps if either `a` or `b` is >= 2^32 - U32Assert2, - /// Peeks `b, a` from the top of the stack, and traps if either `a` or `b` is >= 2^32, raising the given error code - U32Assert2WithError(u32), - /// Peeks `A` from the top of the stack, and traps unless `forall a : A, a < 2^32`, else 0 - U32Assertw, - /// Peeks `A` from the top of the stack, and traps unless `forall a : A, a < 2^32`, else 0, raising the given error code - U32AssertwWithError(u32), - /// Pops `a` from the top of the stack, and places the result of `a mod 2^32` on the stack - /// - /// This is used to cast a field element to the u32 range - U32Cast, - /// Pops `a` from the top of the stack, and splits it into upper and lower 32-bit values, - /// placing them back on the stack. The lower part is calculated as `a mod 2^32`, - /// and the higher part as `a / 2^32`. The higher part will be on top of the stack after. - U32Split, - /// Pops `b, a` from the stack, and places the result of `(a + b) mod 2^32` on the stack, - /// followed by 1 if `(a + b) >= 2^32`, else 0. Thus the first item on the stack will be - /// a boolean indicating whether the arithmetic overflowed, and the second will be the - /// result of the addition. - /// - /// The behavior is undefined if either `b` or `a` are >= 2^32 - U32OverflowingAdd, - /// Same as above, but with `b` provided by the immediate - U32OverflowingAddImm(u32), - /// Pops `b, a` from the stack, and places the result of `(a + b) mod 2^32` on the stack. - /// - /// The behavior is undefined if either `b` or `a` are >= 2^32 - U32WrappingAdd, - /// Same as above, but with `b` provided by the immediate - U32WrappingAddImm(u32), - /// Pops `c, b, a` from the stack, adds them together, and splits the result into higher - /// and lower parts. The lower part is calculated as `(a + b + c) mod 2^32`, - /// the higher part as `(a + b + c) / 2^32`. - /// - /// The behavior is undefined if any of `c`, `b` or `a` are >= 2^32 - U32OverflowingAdd3, - /// Pops `c, b, a` from the stack, adds them together, and splits the result into higher - /// and lower parts. The lower part is calculated as `(a + b + c) mod 2^32`, - /// the higher part as `(a + b + c) / 2^32`. - /// - /// The behavior is undefined if any of `c`, `b` or `a` are >= 2^32 - U32WrappingAdd3, - /// Pops `b, a` from the stack, and places the result of `(a - b) mod 2^32` on the stack, - /// followed by 1 if `a < b`, else 0. Thus the first item on the stack will be - /// a boolean indicating whether the arithmetic underflowed, and the second will be the - /// result of the subtraction. - /// - /// The behavior is undefined if either `b` or `a` are >= 2^32 - U32OverflowingSub, - /// Same as above, but with `b` provided by the immediate - U32OverflowingSubImm(u32), - /// Pops `b, a` from the stack, and places the result of `(a - b) mod 2^32` on the stack. - /// - /// The behavior is undefined if either `b` or `a` are >= 2^32 - U32WrappingSub, - /// Same as above, but with `b` provided by the immediate - U32WrappingSubImm(u32), - /// Pops `b, a` from the stack, and places the result of `(a * b) mod 2^32` on the stack, - /// followed by `(a * b) / 2^32`. Thus the first item on the stack will be the number - /// of times the multiplication overflowed, followed by the result. - /// - /// The behavior is undefined if either `b` or `a` are >= 2^32 - U32OverflowingMul, - /// Same as above, but with `b` provided by the immediate - U32OverflowingMulImm(u32), - /// Pops `b, a` from the stack, and places the result of `(a * b) mod 2^32` on the stack. - /// - /// The behavior is undefined if either `b` or `a` are >= 2^32 - U32WrappingMul, - /// Same as above, but with `b` provided by the immediate - U32WrappingMulImm(u32), - /// Pops `c, b, a` off the stack, and calculates `d = c * b + a`, then splits the result - /// into higher and lower parts, the lower given by `d mod 2^32`, the higher by `d / 2^32`, - /// and pushes them back on the stack, with the higher part on top of the stack at the end. - /// - /// Behavior is undefined if any of `a`, `b`, or `c` are >= 2^32 - U32OverflowingMadd, - /// Pops `c, b, a` off the stack, and pushes `(c * a + b) mod 2^32` on the stack. - /// - /// Behavior is undefined if any of `a`, `b`, or `c` are >= 2^32 - U32WrappingMadd, - /// Pops `b, a` off the stack, and pushes `a / b` on the stack. - /// - /// This operation traps if `b` is zero. - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Div, - /// Same as above, except `b` is provided by the immediate - U32DivImm(u32), - /// Pops `b, a` off the stack, and pushes `a mod b` on the stack. - /// - /// This operation traps if `b` is zero. - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Mod, - /// Same as above, except `b` is provided by the immediate - U32ModImm(u32), - /// Pops `b, a` off the stack, and first pushes `a / b` on the stack, followed by `a mod b`. - /// - /// This operation traps if `b` is zero. - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32DivMod, - /// Same as above, except `b` is provided by the immediate - U32DivModImm(u32), - /// Pops `b, a` off the stack, and places the bitwise AND of `a` and `b` on the stack. - /// - /// Traps if either `a` or `b` >= 2^32 - U32And, - /// Pops `b, a` off the stack, and places the bitwise OR of `a` and `b` on the stack. - /// - /// Traps if either `a` or `b` >= 2^32 - U32Or, - /// Pops `b, a` off the stack, and places the bitwise XOR of `a` and `b` on the stack. - /// - /// Traps if either `a` or `b` >= 2^32 - U32Xor, - /// Pops `a` off the stack, and places the bitwise NOT of `a` on the stack. - /// - /// Traps if `a >= 2^32` - U32Not, - /// Pops `b, a` off the stack, and places the result of `(a * 2^b) mod 2^32` on the stack. - /// - /// Truncates if the shift would cause overflow. - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Shl, - /// Same as above, except `b` is provided by the immediate - U32ShlImm(u32), - /// Pops `b, a` off the stack, and places the result of `a / 2^b` on the stack. - /// - /// Truncates if the shift would cause overflow. - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Shr, - /// Same as above, except `b` is provided by the immediate - U32ShrImm(u32), - /// Pops `b, a` off the stack, and places the result of rotating the 32-bit - /// representation of `a` to the left by `b` bits. - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Rotl, - /// Same as above, except `b` is provided by the immediate - U32RotlImm(u32), - /// Pops `b, a` off the stack, and places the result of rotating the 32-bit - /// representation of `a` to the right by `b` bits. - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Rotr, - /// Same as above, except `b` is provided by the immediate - U32RotrImm(u32), - /// Pops `a` off the stack, and places the number of set bits in `a` (it's hamming weight). - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Popcnt, - /// Pops `b, a` from the stack, and places 1 on the stack if `a < b`, else 0 - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Lt, - /// Pops `b, a` from the stack, and places 1 on the stack if `a <= b`, else 0 - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Lte, - /// Pops `b, a` from the stack, and places 1 on the stack if `a > b`, else 0 - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Gt, - /// Pops `b, a` from the stack, and places 1 on the stack if `a >= b`, else 0 - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Gte, - /// Pops `b, a` from the stack, and places `a` back on the stack if `a < b`, else `b` - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Min, - /// Pops `b, a` from the stack, and places `a` back on the stack if `a > b`, else `b` - /// - /// This operation is unchecked, so the result is undefined if the operands are not valid u32 - U32Max, -} -impl MasmOp { - pub fn from_masm( - ix: miden_assembly::ast::Instruction, - locals: &[FunctionIdent], - imported: &miden_assembly::ast::ModuleImports, - ) -> SmallVec<[Self; 2]> { - use crate::{StarkField, Symbol}; - use miden_assembly::ast::Instruction; - - let op = match ix { - Instruction::Assert => Self::Assert, - Instruction::AssertWithError(code) => Self::AssertWithError(code), - Instruction::AssertEq => Self::AssertEq, - Instruction::AssertEqWithError(code) => Self::AssertEqWithError(code), - Instruction::AssertEqw => Self::AssertEqw, - Instruction::AssertEqwWithError(code) => Self::AssertEqwWithError(code), - Instruction::Assertz => Self::Assertz, - Instruction::AssertzWithError(code) => Self::AssertzWithError(code), - Instruction::Add => Self::Add, - Instruction::AddImm(imm) => Self::AddImm(imm), - Instruction::Sub => Self::Sub, - Instruction::SubImm(imm) => Self::SubImm(imm), - Instruction::Mul => Self::Mul, - Instruction::MulImm(imm) => Self::MulImm(imm), - Instruction::Div => Self::Div, - Instruction::DivImm(imm) => Self::DivImm(imm), - Instruction::Neg => Self::Neg, - Instruction::Inv => Self::Inv, - Instruction::Incr => Self::Incr, - Instruction::Pow2 => Self::Pow2, - Instruction::Exp => Self::Exp, - Instruction::ExpImm(imm) => { - Self::ExpImm(imm.as_int().try_into().expect("invalid exponent")) - } - Instruction::ExpBitLength(imm) => Self::ExpImm(imm), - Instruction::Not => Self::Not, - Instruction::And => Self::And, - Instruction::Or => Self::Or, - Instruction::Xor => Self::Xor, - Instruction::Eq => Self::Eq, - Instruction::EqImm(imm) => Self::EqImm(imm), - Instruction::Neq => Self::Neq, - Instruction::NeqImm(imm) => Self::NeqImm(imm), - Instruction::Eqw => Self::Eqw, - Instruction::Lt => Self::Lt, - Instruction::Lte => Self::Lte, - Instruction::Gt => Self::Gt, - Instruction::Gte => Self::Gte, - Instruction::IsOdd => Self::IsOdd, - Instruction::Ext2Add - | Instruction::Ext2Sub - | Instruction::Ext2Mul - | Instruction::Ext2Div - | Instruction::Ext2Neg - | Instruction::Ext2Inv => unimplemented!(), - Instruction::U32Test => Self::U32Test, - Instruction::U32TestW => Self::U32Testw, - Instruction::U32Assert => Self::U32Assert, - Instruction::U32AssertWithError(code) => Self::U32AssertWithError(code), - Instruction::U32Assert2 => Self::U32Assert2, - Instruction::U32Assert2WithError(code) => Self::U32Assert2WithError(code), - Instruction::U32AssertW => Self::U32Assertw, - Instruction::U32AssertWWithError(code) => Self::U32AssertwWithError(code), - Instruction::U32Split => Self::U32Split, - Instruction::U32Cast => Self::U32Cast, - Instruction::U32WrappingAdd => Self::U32WrappingAdd, - Instruction::U32WrappingAddImm(imm) => Self::U32WrappingAddImm(imm), - Instruction::U32OverflowingAdd => Self::U32OverflowingAdd, - Instruction::U32OverflowingAddImm(imm) => Self::U32OverflowingAddImm(imm), - Instruction::U32OverflowingAdd3 => Self::U32OverflowingAdd3, - Instruction::U32WrappingAdd3 => Self::U32WrappingAdd3, - Instruction::U32WrappingSub => Self::U32WrappingSub, - Instruction::U32WrappingSubImm(imm) => Self::U32WrappingSubImm(imm), - Instruction::U32OverflowingSub => Self::U32OverflowingSub, - Instruction::U32OverflowingSubImm(imm) => Self::U32OverflowingSubImm(imm), - Instruction::U32WrappingMul => Self::U32WrappingMul, - Instruction::U32WrappingMulImm(imm) => Self::U32WrappingMulImm(imm), - Instruction::U32OverflowingMul => Self::U32OverflowingMul, - Instruction::U32OverflowingMulImm(imm) => Self::U32OverflowingMulImm(imm), - Instruction::U32OverflowingMadd => Self::U32OverflowingMadd, - Instruction::U32WrappingMadd => Self::U32WrappingMadd, - Instruction::U32Div => Self::U32Div, - Instruction::U32DivImm(imm) => Self::U32DivImm(imm), - Instruction::U32Mod => Self::U32Mod, - Instruction::U32ModImm(imm) => Self::U32ModImm(imm), - Instruction::U32DivMod => Self::U32DivMod, - Instruction::U32DivModImm(imm) => Self::U32DivModImm(imm), - Instruction::U32And => Self::U32And, - Instruction::U32Or => Self::U32Or, - Instruction::U32Xor => Self::U32Xor, - Instruction::U32Not => Self::U32Not, - Instruction::U32Shr => Self::U32Shr, - Instruction::U32ShrImm(imm) => Self::U32ShrImm(imm as u32), - Instruction::U32Shl => Self::U32Shl, - Instruction::U32ShlImm(imm) => Self::U32ShlImm(imm as u32), - Instruction::U32Rotr => Self::U32Rotr, - Instruction::U32RotrImm(imm) => Self::U32RotrImm(imm as u32), - Instruction::U32Rotl => Self::U32Rotl, - Instruction::U32RotlImm(imm) => Self::U32RotlImm(imm as u32), - Instruction::U32Popcnt => Self::U32Popcnt, - Instruction::U32Lt => Self::U32Lt, - Instruction::U32Lte => Self::U32Lte, - Instruction::U32Gt => Self::U32Gt, - Instruction::U32Gte => Self::U32Gte, - Instruction::U32Min => Self::U32Min, - Instruction::U32Max => Self::U32Max, - Instruction::Drop => Self::Drop, - Instruction::DropW => Self::Dropw, - Instruction::PadW => Self::Padw, - Instruction::Dup0 => Self::Dup(0), - Instruction::Dup1 => Self::Dup(1), - Instruction::Dup2 => Self::Dup(2), - Instruction::Dup3 => Self::Dup(3), - Instruction::Dup4 => Self::Dup(4), - Instruction::Dup5 => Self::Dup(5), - Instruction::Dup6 => Self::Dup(6), - Instruction::Dup7 => Self::Dup(7), - Instruction::Dup8 => Self::Dup(8), - Instruction::Dup9 => Self::Dup(9), - Instruction::Dup10 => Self::Dup(10), - Instruction::Dup11 => Self::Dup(11), - Instruction::Dup12 => Self::Dup(12), - Instruction::Dup13 => Self::Dup(13), - Instruction::Dup14 => Self::Dup(14), - Instruction::Dup15 => Self::Dup(15), - Instruction::DupW0 => Self::Dupw(0), - Instruction::DupW1 => Self::Dupw(1), - Instruction::DupW2 => Self::Dupw(2), - Instruction::DupW3 => Self::Dupw(3), - Instruction::Swap1 => Self::Swap(1), - Instruction::Swap2 => Self::Swap(2), - Instruction::Swap3 => Self::Swap(3), - Instruction::Swap4 => Self::Swap(4), - Instruction::Swap5 => Self::Swap(5), - Instruction::Swap6 => Self::Swap(6), - Instruction::Swap7 => Self::Swap(7), - Instruction::Swap8 => Self::Swap(8), - Instruction::Swap9 => Self::Swap(9), - Instruction::Swap10 => Self::Swap(10), - Instruction::Swap11 => Self::Swap(11), - Instruction::Swap12 => Self::Swap(12), - Instruction::Swap13 => Self::Swap(13), - Instruction::Swap14 => Self::Swap(14), - Instruction::Swap15 => Self::Swap(15), - Instruction::SwapW1 => Self::Swapw(1), - Instruction::SwapW2 => Self::Swapw(2), - Instruction::SwapW3 => Self::Swapw(3), - Instruction::SwapDw => unimplemented!("swap double-word"), - Instruction::MovUp2 => Self::Movup(2), - Instruction::MovUp3 => Self::Movup(3), - Instruction::MovUp4 => Self::Movup(4), - Instruction::MovUp5 => Self::Movup(5), - Instruction::MovUp6 => Self::Movup(6), - Instruction::MovUp7 => Self::Movup(7), - Instruction::MovUp8 => Self::Movup(8), - Instruction::MovUp9 => Self::Movup(9), - Instruction::MovUp10 => Self::Movup(10), - Instruction::MovUp11 => Self::Movup(11), - Instruction::MovUp12 => Self::Movup(12), - Instruction::MovUp13 => Self::Movup(13), - Instruction::MovUp14 => Self::Movup(14), - Instruction::MovUp15 => Self::Movup(15), - Instruction::MovUpW2 => Self::Movupw(2), - Instruction::MovUpW3 => Self::Movupw(3), - Instruction::MovDn2 => Self::Movdn(2), - Instruction::MovDn3 => Self::Movdn(3), - Instruction::MovDn4 => Self::Movdn(4), - Instruction::MovDn5 => Self::Movdn(5), - Instruction::MovDn6 => Self::Movdn(6), - Instruction::MovDn7 => Self::Movdn(7), - Instruction::MovDn8 => Self::Movdn(8), - Instruction::MovDn9 => Self::Movdn(9), - Instruction::MovDn10 => Self::Movdn(10), - Instruction::MovDn11 => Self::Movdn(11), - Instruction::MovDn12 => Self::Movdn(12), - Instruction::MovDn13 => Self::Movdn(13), - Instruction::MovDn14 => Self::Movdn(14), - Instruction::MovDn15 => Self::Movdn(15), - Instruction::MovDnW2 => Self::Movdnw(2), - Instruction::MovDnW3 => Self::Movdnw(3), - Instruction::CSwap => Self::Cswap, - Instruction::CSwapW => Self::Cswapw, - Instruction::CDrop => Self::Cdrop, - Instruction::CDropW => Self::Cdropw, - Instruction::PushU8(elem) => Self::PushU8(elem), - Instruction::PushU16(elem) => Self::PushU32(elem as u32), - Instruction::PushU32(elem) => Self::PushU32(elem), - Instruction::PushFelt(elem) => Self::Push(elem), - Instruction::PushWord(word) => Self::Pushw(word), - Instruction::PushU8List(u8s) => return u8s.into_iter().map(Self::PushU8).collect(), - Instruction::PushU16List(u16s) => { - return u16s.into_iter().map(|i| Self::PushU32(i as u32)).collect() - } - Instruction::PushU32List(u32s) => return u32s.into_iter().map(Self::PushU32).collect(), - Instruction::PushFeltList(felts) => return felts.into_iter().map(Self::Push).collect(), - Instruction::Locaddr(id) => { - Self::LocAddr(LocalId::from_u8(id.try_into().expect("invalid local id"))) - } - Instruction::LocStore(id) => { - Self::LocStore(LocalId::from_u8(id.try_into().expect("invalid local id"))) - } - Instruction::LocStoreW(id) => { - Self::LocStorew(LocalId::from_u8(id.try_into().expect("invalid local id"))) - } - Instruction::Clk => Self::Clk, - Instruction::MemLoad => Self::MemLoad, - Instruction::MemLoadImm(addr) => Self::MemLoadImm(addr), - Instruction::MemLoadW => Self::MemLoadw, - Instruction::MemLoadWImm(addr) => Self::MemLoadwImm(addr), - Instruction::MemStore => Self::MemStore, - Instruction::MemStoreImm(addr) => Self::MemStoreImm(addr), - Instruction::MemStoreW => Self::MemStorew, - Instruction::MemStoreWImm(addr) => Self::MemStorewImm(addr), - Instruction::LocLoad(_) | Instruction::LocLoadW(_) => { - unimplemented!("load by local id") - } - Instruction::MemStream => Self::MemStream, - Instruction::AdvPipe => Self::AdvPipe, - Instruction::AdvPush(byte) => Self::AdvPush(byte), - Instruction::AdvLoadW => Self::AdvLoadw, - Instruction::AdvInject(_) => unimplemented!("adv_inject"), - Instruction::Hash - | Instruction::HMerge - | Instruction::HPerm - | Instruction::MTreeGet - | Instruction::MTreeSet - | Instruction::MTreeMerge - | Instruction::MTreeVerify => unimplemented!("cryptographic operations"), - Instruction::ExecLocal(local_index) => Self::Exec(locals[local_index as usize]), - Instruction::ExecImported(ref proc_id) => { - let module = imported - .get_procedure_path(proc_id) - .expect("reference to import that doesn't exist") - .last(); - let name = imported - .get_procedure_name(proc_id) - .expect("reference to import that doesn't exist"); - Self::Exec(FunctionIdent { - module: Ident::with_empty_span(Symbol::intern(module)), - function: Ident::with_empty_span(Symbol::intern(name.as_ref())), - }) - } - Instruction::CallLocal(_) - | Instruction::CallMastRoot(_) - | Instruction::CallImported(_) => unimplemented!("contract calls"), - Instruction::SysCall(ref proc_id) => { - let module = imported - .get_procedure_path(proc_id) - .expect("reference to import that doesn't exist") - .last(); - let name = imported - .get_procedure_name(proc_id) - .expect("reference to import that doesn't exist"); - Self::Syscall(FunctionIdent { - module: Ident::with_empty_span(Symbol::intern(module)), - function: Ident::with_empty_span(Symbol::intern(name.as_ref())), - }) - } - Instruction::DynExec => Self::DynExec, - Instruction::DynCall => Self::DynCall, - Instruction::ProcRefLocal(local_index) => Self::ProcRef(locals[local_index as usize]), - Instruction::ProcRefImported(ref proc_id) => { - let module = imported - .get_procedure_path(proc_id) - .expect("reference to import that doesn't exist") - .last(); - let name = imported - .get_procedure_name(proc_id) - .expect("reference to import that doesn't exist"); - Self::ProcRef(FunctionIdent { - module: Ident::with_empty_span(Symbol::intern(module)), - function: Ident::with_empty_span(Symbol::intern(name.as_ref())), - }) - } - Instruction::Caller => Self::Caller, - Instruction::Sdepth - | Instruction::FriExt2Fold4 - | Instruction::Breakpoint - | Instruction::Emit(_) - | Instruction::Debug(_) => unimplemented!("miscellaneous instructions"), - }; - smallvec![op] - } - - pub fn into_node( - self, - _codemap: &miden_diagnostics::CodeMap, - imports: &super::ModuleImportInfo, - local_ids: &FxHashMap, - proc_ids: &FxHashMap, - ) -> SmallVec<[miden_assembly::ast::Node; 2]> { - use miden_assembly::ast::{Instruction, Node}; - let node = match self { - Self::Padw => Instruction::PadW, - Self::Push(v) => Instruction::PushFelt(v), - Self::Push2([a, b]) => { - return smallvec![ - Node::Instruction(Instruction::PushFelt(a)), - Node::Instruction(Instruction::PushFelt(b)) - ] - } - Self::Pushw(word) => Instruction::PushWord(word), - Self::PushU8(v) => Instruction::PushFelt(Felt::new(v as u64)), - Self::PushU16(v) => Instruction::PushFelt(Felt::new(v as u64)), - Self::PushU32(v) => Instruction::PushFelt(Felt::new(v as u64)), - Self::Drop => Instruction::Drop, - Self::Dropw => Instruction::DropW, - Self::Dup(0) => Instruction::Dup0, - Self::Dup(1) => Instruction::Dup1, - Self::Dup(2) => Instruction::Dup2, - Self::Dup(3) => Instruction::Dup3, - Self::Dup(4) => Instruction::Dup4, - Self::Dup(5) => Instruction::Dup5, - Self::Dup(6) => Instruction::Dup6, - Self::Dup(7) => Instruction::Dup7, - Self::Dup(8) => Instruction::Dup8, - Self::Dup(9) => Instruction::Dup9, - Self::Dup(10) => Instruction::Dup10, - Self::Dup(11) => Instruction::Dup11, - Self::Dup(12) => Instruction::Dup12, - Self::Dup(13) => Instruction::Dup13, - Self::Dup(14) => Instruction::Dup14, - Self::Dup(15) => Instruction::Dup15, - Self::Dup(n) => { - panic!("invalid dup instruction, valid index range is 0..=15, got {n}") - } - Self::Dupw(0) => Instruction::DupW0, - Self::Dupw(1) => Instruction::DupW1, - Self::Dupw(2) => Instruction::DupW2, - Self::Dupw(3) => Instruction::DupW3, - Self::Dupw(n) => { - panic!("invalid dupw instruction, valid index range is 0..=3, got {n}") - } - Self::Swap(1) => Instruction::Swap1, - Self::Swap(2) => Instruction::Swap2, - Self::Swap(3) => Instruction::Swap3, - Self::Swap(4) => Instruction::Swap4, - Self::Swap(5) => Instruction::Swap5, - Self::Swap(6) => Instruction::Swap6, - Self::Swap(7) => Instruction::Swap7, - Self::Swap(8) => Instruction::Swap8, - Self::Swap(9) => Instruction::Swap9, - Self::Swap(10) => Instruction::Swap10, - Self::Swap(11) => Instruction::Swap11, - Self::Swap(12) => Instruction::Swap12, - Self::Swap(13) => Instruction::Swap13, - Self::Swap(14) => Instruction::Swap14, - Self::Swap(15) => Instruction::Swap15, - Self::Swap(n) => { - panic!("invalid swap instruction, valid index range is 1..=15, got {n}") - } - Self::Swapw(1) => Instruction::SwapW1, - Self::Swapw(2) => Instruction::SwapW2, - Self::Swapw(3) => Instruction::SwapW3, - Self::Swapw(n) => { - panic!("invalid swapw instruction, valid index range is 1..=3, got {n}") - } - Self::Movup(2) => Instruction::MovUp2, - Self::Movup(3) => Instruction::MovUp3, - Self::Movup(4) => Instruction::MovUp4, - Self::Movup(5) => Instruction::MovUp5, - Self::Movup(6) => Instruction::MovUp6, - Self::Movup(7) => Instruction::MovUp7, - Self::Movup(8) => Instruction::MovUp8, - Self::Movup(9) => Instruction::MovUp9, - Self::Movup(10) => Instruction::MovUp10, - Self::Movup(11) => Instruction::MovUp11, - Self::Movup(12) => Instruction::MovUp12, - Self::Movup(13) => Instruction::MovUp13, - Self::Movup(14) => Instruction::MovUp14, - Self::Movup(15) => Instruction::MovUp15, - Self::Movup(n) => { - panic!("invalid movup instruction, valid index range is 2..=15, got {n}") - } - Self::Movupw(2) => Instruction::MovUpW2, - Self::Movupw(3) => Instruction::MovUpW3, - Self::Movupw(n) => { - panic!("invalid movupw instruction, valid index range is 2..=3, got {n}") - } - Self::Movdn(2) => Instruction::MovDn2, - Self::Movdn(3) => Instruction::MovDn3, - Self::Movdn(4) => Instruction::MovDn4, - Self::Movdn(5) => Instruction::MovDn5, - Self::Movdn(6) => Instruction::MovDn6, - Self::Movdn(7) => Instruction::MovDn7, - Self::Movdn(8) => Instruction::MovDn8, - Self::Movdn(9) => Instruction::MovDn9, - Self::Movdn(10) => Instruction::MovDn10, - Self::Movdn(11) => Instruction::MovDn11, - Self::Movdn(12) => Instruction::MovDn12, - Self::Movdn(13) => Instruction::MovDn13, - Self::Movdn(14) => Instruction::MovDn14, - Self::Movdn(15) => Instruction::MovDn15, - Self::Movdn(n) => { - panic!("invalid movdn instruction, valid index range is 2..=15, got {n}") - } - Self::Movdnw(2) => Instruction::MovDnW2, - Self::Movdnw(3) => Instruction::MovDnW3, - Self::Movdnw(n) => { - panic!("invalid movdnw instruction, valid index range is 2..=3, got {n}") - } - Self::Cswap => Instruction::CSwap, - Self::Cswapw => Instruction::CSwapW, - Self::Cdrop => Instruction::CDrop, - Self::Cdropw => Instruction::CDropW, - Self::Assert => Instruction::Assert, - Self::AssertWithError(code) => Instruction::AssertWithError(code), - Self::Assertz => Instruction::Assertz, - Self::AssertzWithError(code) => Instruction::AssertzWithError(code), - Self::AssertEq => Instruction::AssertEq, - Self::AssertEqWithError(code) => Instruction::AssertEqWithError(code), - Self::AssertEqw => Instruction::AssertEqw, - Self::AssertEqwWithError(code) => Instruction::AssertEqwWithError(code), - Self::LocAddr(id) => Instruction::Locaddr(id.as_usize() as u16), - Self::LocStore(id) => Instruction::LocStore(id.as_usize() as u16), - Self::LocStorew(id) => Instruction::LocStoreW(id.as_usize() as u16), - Self::MemLoad => Instruction::MemLoad, - Self::MemLoadImm(addr) => Instruction::MemLoadImm(addr), - Self::MemLoadw => Instruction::MemLoadW, - Self::MemLoadwImm(addr) => Instruction::MemLoadWImm(addr), - Self::MemStore => Instruction::MemStore, - Self::MemStoreImm(addr) => Instruction::MemStoreImm(addr), - Self::MemStorew => Instruction::MemStoreW, - Self::MemStorewImm(addr) => Instruction::MemStoreWImm(addr), - Self::MemLoadOffset - | Self::MemLoadOffsetImm(_, _) - | Self::MemStoreOffset - | Self::MemStoreOffsetImm(_, _) => unimplemented!( - "this is an experimental instruction that is not supported by the Miden VM" - ), - Self::MemStream => Instruction::MemStream, - Self::AdvPipe => Instruction::AdvPipe, - Self::AdvPush(n) => Instruction::AdvPush(n), - Self::AdvLoadw => Instruction::AdvLoadW, - Self::If(_, _) | Self::While(_) | Self::Repeat(_, _) => { - panic!("control flow instructions are meant to be handled specially by the caller") - } - Self::Exec(ref callee) => { - if let Some(idx) = local_ids.get(callee).copied() { - Instruction::ExecLocal(idx) - } else { - let aliased = if let Some(alias) = imports.alias(&callee.module) { - FunctionIdent { - module: alias, - function: callee.function, - } - } else { - let module_as_import = super::MasmImport::try_from(callee.module) - .expect("invalid module name"); - FunctionIdent { - module: Ident::with_empty_span(module_as_import.alias), - function: callee.function, - } - }; - let id = proc_ids - .get(&aliased) - .copied() - .unwrap_or_else(|| miden_assembly::ProcedureId::new(aliased.to_string())); - Instruction::ExecImported(id) - } - } - Self::Syscall(ref callee) => { - let aliased = if let Some(alias) = imports.alias(&callee.module) { - FunctionIdent { - module: alias, - function: callee.function, - } - } else { - let module_as_import = - super::MasmImport::try_from(callee.module).expect("invalid module name"); - FunctionIdent { - module: Ident::with_empty_span(module_as_import.alias), - function: callee.function, - } - }; - let id = proc_ids - .get(&aliased) - .copied() - .unwrap_or_else(|| miden_assembly::ProcedureId::new(aliased.to_string())); - Instruction::SysCall(id) - } - Self::DynExec => Instruction::DynExec, - Self::DynCall => Instruction::DynCall, - Self::ProcRef(ref callee) => { - if let Some(idx) = local_ids.get(callee).copied() { - Instruction::ProcRefLocal(idx) - } else { - let aliased = if let Some(alias) = imports.alias(&callee.module) { - FunctionIdent { - module: alias, - function: callee.function, - } - } else { - let module_as_import = super::MasmImport::try_from(callee.module) - .expect("invalid module name"); - FunctionIdent { - module: Ident::with_empty_span(module_as_import.alias), - function: callee.function, - } - }; - let id = proc_ids - .get(&aliased) - .copied() - .unwrap_or_else(|| miden_assembly::ProcedureId::new(aliased.to_string())); - Instruction::ProcRefImported(id) - } - } - Self::Add => Instruction::Add, - Self::AddImm(imm) => Instruction::AddImm(imm), - Self::Sub => Instruction::Sub, - Self::SubImm(imm) => Instruction::SubImm(imm), - Self::Mul => Instruction::Mul, - Self::MulImm(imm) => Instruction::MulImm(imm), - Self::Div => Instruction::Div, - Self::DivImm(imm) => Instruction::DivImm(imm), - Self::Neg => Instruction::Neg, - Self::Inv => Instruction::Inv, - Self::Incr => Instruction::Incr, - Self::Pow2 => Instruction::Pow2, - Self::Exp => Instruction::Exp, - Self::ExpImm(imm) => Instruction::ExpBitLength(imm), - Self::Not => Instruction::Not, - Self::And => Instruction::And, - Self::AndImm(imm) => { - return smallvec![ - Node::Instruction(Instruction::PushFelt(Felt::new(imm as u64))), - Node::Instruction(Instruction::And) - ] - } - Self::Or => Instruction::Or, - Self::OrImm(imm) => { - return smallvec![ - Node::Instruction(Instruction::PushFelt(Felt::new(imm as u64))), - Node::Instruction(Instruction::Or) - ] - } - Self::Xor => Instruction::Xor, - Self::XorImm(imm) => { - return smallvec![ - Node::Instruction(Instruction::PushFelt(Felt::new(imm as u64))), - Node::Instruction(Instruction::Xor) - ] - } - Self::Eq => Instruction::Eq, - Self::EqImm(imm) => Instruction::EqImm(imm), - Self::Neq => Instruction::Neq, - Self::NeqImm(imm) => Instruction::NeqImm(imm), - Self::Gt => Instruction::Gt, - Self::GtImm(imm) => { - return smallvec![ - Node::Instruction(Instruction::PushFelt(imm)), - Node::Instruction(Instruction::Gt) - ] - } - Self::Gte => Instruction::Gte, - Self::GteImm(imm) => { - return smallvec![ - Node::Instruction(Instruction::PushFelt(imm)), - Node::Instruction(Instruction::Gte) - ] - } - Self::Lt => Instruction::Lt, - Self::LtImm(imm) => { - return smallvec![ - Node::Instruction(Instruction::PushFelt(imm)), - Node::Instruction(Instruction::Lt) - ] - } - Self::Lte => Instruction::Lte, - Self::LteImm(imm) => { - return smallvec![ - Node::Instruction(Instruction::PushFelt(imm)), - Node::Instruction(Instruction::Lte) - ] - } - Self::IsOdd => Instruction::IsOdd, - Self::Eqw => Instruction::Eqw, - Self::Clk => Instruction::Clk, - Self::Caller => Instruction::Caller, - Self::U32Test => Instruction::U32Test, - Self::U32Testw => Instruction::U32TestW, - Self::U32Assert => Instruction::U32Assert, - Self::U32AssertWithError(code) => Instruction::U32AssertWithError(code), - Self::U32Assert2 => Instruction::U32Assert2, - Self::U32Assert2WithError(code) => Instruction::U32Assert2WithError(code), - Self::U32Assertw => Instruction::U32AssertW, - Self::U32AssertwWithError(code) => Instruction::U32AssertWWithError(code), - Self::U32Cast => Instruction::U32Cast, - Self::U32Split => Instruction::U32Split, - Self::U32OverflowingAdd => Instruction::U32OverflowingAdd, - Self::U32OverflowingAddImm(imm) => Instruction::U32OverflowingAddImm(imm), - Self::U32WrappingAdd => Instruction::U32WrappingAdd, - Self::U32WrappingAddImm(imm) => Instruction::U32WrappingAddImm(imm), - Self::U32OverflowingAdd3 => Instruction::U32OverflowingAdd3, - Self::U32WrappingAdd3 => Instruction::U32WrappingAdd3, - Self::U32OverflowingSub => Instruction::U32OverflowingSub, - Self::U32OverflowingSubImm(imm) => Instruction::U32OverflowingSubImm(imm), - Self::U32WrappingSub => Instruction::U32WrappingSub, - Self::U32WrappingSubImm(imm) => Instruction::U32WrappingSubImm(imm), - Self::U32OverflowingMul => Instruction::U32OverflowingMul, - Self::U32OverflowingMulImm(imm) => Instruction::U32OverflowingMulImm(imm), - Self::U32WrappingMul => Instruction::U32WrappingMul, - Self::U32WrappingMulImm(imm) => Instruction::U32WrappingMulImm(imm), - Self::U32OverflowingMadd => Instruction::U32OverflowingMadd, - Self::U32WrappingMadd => Instruction::U32WrappingMadd, - Self::U32Div => Instruction::U32Div, - Self::U32DivImm(imm) => Instruction::U32DivImm(imm), - Self::U32Mod => Instruction::U32Mod, - Self::U32ModImm(imm) => Instruction::U32ModImm(imm), - Self::U32DivMod => Instruction::U32DivMod, - Self::U32DivModImm(imm) => Instruction::U32DivModImm(imm), - Self::U32And => Instruction::U32And, - Self::U32Or => Instruction::U32Or, - Self::U32Xor => Instruction::U32Xor, - Self::U32Not => Instruction::U32Not, - Self::U32Shl => Instruction::U32Shl, - Self::U32ShlImm(imm) => { - Instruction::U32ShlImm(imm.try_into().expect("invalid rotation")) - } - Self::U32Shr => Instruction::U32Shr, - Self::U32ShrImm(imm) => { - Instruction::U32ShrImm(imm.try_into().expect("invalid rotation")) - } - Self::U32Rotl => Instruction::U32Rotl, - Self::U32RotlImm(imm) => { - Instruction::U32RotlImm(imm.try_into().expect("invalid rotation")) - } - Self::U32Rotr => Instruction::U32Rotr, - Self::U32RotrImm(imm) => { - Instruction::U32RotrImm(imm.try_into().expect("invalid rotation")) - } - Self::U32Popcnt => Instruction::U32Popcnt, - Self::U32Lt => Instruction::U32Lt, - Self::U32Lte => Instruction::U32Lte, - Self::U32Gt => Instruction::U32Gt, - Self::U32Gte => Instruction::U32Gte, - Self::U32Min => Instruction::U32Min, - Self::U32Max => Instruction::U32Max, - }; - smallvec![Node::Instruction(node)] - } -} - -/// This implementation displays the opcode name for the given [MasmOp] -impl fmt::Display for MasmOp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Padw => f.write_str("padw"), - Self::Push(_) - | Self::Push2(_) - | Self::Pushw(_) - | Self::PushU8(_) - | Self::PushU16(_) - | Self::PushU32(_) => f.write_str("push"), - Self::Drop => f.write_str("drop"), - Self::Dropw => f.write_str("dropw"), - Self::Dup(_) => f.write_str("dup"), - Self::Dupw(_) => f.write_str("dupw"), - Self::Swap(_) => f.write_str("swap"), - Self::Swapw(_) => f.write_str("swapw"), - Self::Movup(_) => f.write_str("movup"), - Self::Movupw(_) => f.write_str("movupw"), - Self::Movdn(_) => f.write_str("movdn"), - Self::Movdnw(_) => f.write_str("movdnw"), - Self::Cswap => f.write_str("cswap"), - Self::Cswapw => f.write_str("cswapw"), - Self::Cdrop => f.write_str("cdrop"), - Self::Cdropw => f.write_str("cdropw"), - Self::Assert => f.write_str("assert"), - Self::AssertWithError(code) => write!(f, "assert.err={code}"), - Self::Assertz => f.write_str("assertz"), - Self::AssertzWithError(code) => write!(f, "assertz.err={code}"), - Self::AssertEq => f.write_str("assert_eq"), - Self::AssertEqWithError(code) => write!(f, "assert_eq.err={code}"), - Self::AssertEqw => f.write_str("assert_eqw"), - Self::AssertEqwWithError(code) => write!(f, "assert_eqw.err={code}"), - Self::LocAddr(_) => f.write_str("locaddr"), - Self::LocStore(_) => f.write_str("loc_store"), - Self::LocStorew(_) => f.write_str("loc_storew"), - Self::MemLoad - | Self::MemLoadOffset - | Self::MemLoadImm(_) - | Self::MemLoadOffsetImm(_, _) => f.write_str("mem_load"), - Self::MemLoadw | Self::MemLoadwImm(_) => f.write_str("mem_loadw"), - Self::MemStore - | Self::MemStoreOffset - | Self::MemStoreImm(_) - | Self::MemStoreOffsetImm(_, _) => f.write_str("mem_store"), - Self::MemStorew | Self::MemStorewImm(_) => f.write_str("mem_storew"), - Self::MemStream => f.write_str("mem_stream"), - Self::AdvPipe => f.write_str("adv_pipe"), - Self::AdvPush(_) => f.write_str("adv_push"), - Self::AdvLoadw => f.write_str("adv_loadw"), - Self::If(_, _) => f.write_str("if.true"), - Self::While(_) => f.write_str("while.true"), - Self::Repeat(_, _) => f.write_str("repeat"), - Self::Exec(_) => f.write_str("exec"), - Self::Syscall(_) => f.write_str("syscall"), - Self::DynExec => f.write_str("dynexec"), - Self::DynCall => f.write_str("dyncall"), - Self::ProcRef(_) => f.write_str("procref"), - Self::Add | Self::AddImm(_) => f.write_str("add"), - Self::Sub | Self::SubImm(_) => f.write_str("sub"), - Self::Mul | Self::MulImm(_) => f.write_str("mul"), - Self::Div | Self::DivImm(_) => f.write_str("div"), - Self::Neg => f.write_str("neg"), - Self::Inv => f.write_str("inv"), - Self::Incr => f.write_str("add.1"), - Self::Pow2 => f.write_str("pow2"), - Self::Exp => f.write_str("exp"), - Self::ExpImm(imm) => write!(f, "exp.u{imm}"), - Self::Not => f.write_str("not"), - Self::And | Self::AndImm(_) => f.write_str("and"), - Self::Or | Self::OrImm(_) => f.write_str("or"), - Self::Xor | Self::XorImm(_) => f.write_str("xor"), - Self::Eq | Self::EqImm(_) => f.write_str("eq"), - Self::Neq | Self::NeqImm(_) => f.write_str("neq"), - Self::Gt | Self::GtImm(_) => f.write_str("gt"), - Self::Gte | Self::GteImm(_) => f.write_str("gte"), - Self::Lt | Self::LtImm(_) => f.write_str("lt"), - Self::Lte | Self::LteImm(_) => f.write_str("lte"), - Self::IsOdd => f.write_str("is_odd"), - Self::Eqw => f.write_str("eqw"), - Self::Clk => f.write_str("clk"), - Self::Caller => f.write_str("caller"), - Self::U32Test => f.write_str("u32test"), - Self::U32Testw => f.write_str("u32testw"), - Self::U32Assert => f.write_str("u32assert"), - Self::U32AssertWithError(code) => write!(f, "u32assert.err={code}"), - Self::U32Assert2 => f.write_str("u32assert2"), - Self::U32Assert2WithError(code) => write!(f, "u32assert2.err={code}"), - Self::U32Assertw => f.write_str("u32assertw"), - Self::U32AssertwWithError(code) => write!(f, "u32assertw.err={code}"), - Self::U32Cast => f.write_str("u32cast"), - Self::U32Split => f.write_str("u32split"), - Self::U32OverflowingAdd | Self::U32OverflowingAddImm(_) => { - f.write_str("u32overflowing_add") - } - Self::U32WrappingAdd | Self::U32WrappingAddImm(_) => f.write_str("u32wrapping_add"), - Self::U32OverflowingAdd3 => f.write_str("u32overflowing_add3"), - Self::U32WrappingAdd3 => f.write_str("u32wrapping_add3"), - Self::U32OverflowingSub | Self::U32OverflowingSubImm(_) => { - f.write_str("u32overflowing_sub") - } - Self::U32WrappingSub | Self::U32WrappingSubImm(_) => f.write_str("u32wrapping_sub"), - Self::U32OverflowingMul | Self::U32OverflowingMulImm(_) => { - f.write_str("u32overflowing_mul") - } - Self::U32WrappingMul | Self::U32WrappingMulImm(_) => f.write_str("u32wrapping_mul"), - Self::U32OverflowingMadd => f.write_str("u32overflowing_madd"), - Self::U32WrappingMadd => f.write_str("u32wrapping_madd"), - Self::U32Div | Self::U32DivImm(_) => f.write_str("u32div"), - Self::U32Mod | Self::U32ModImm(_) => f.write_str("u32mod"), - Self::U32DivMod | Self::U32DivModImm(_) => f.write_str("u32divmod"), - Self::U32And => f.write_str("u32and"), - Self::U32Or => f.write_str("u32or"), - Self::U32Xor => f.write_str("u32xor"), - Self::U32Not => f.write_str("u32not"), - Self::U32Shl | Self::U32ShlImm(_) => f.write_str("u32shl"), - Self::U32Shr | Self::U32ShrImm(_) => f.write_str("u32shr"), - Self::U32Rotl | Self::U32RotlImm(_) => f.write_str("u32rotl"), - Self::U32Rotr | Self::U32RotrImm(_) => f.write_str("u32rotr"), - Self::U32Popcnt => f.write_str("u32popcnt"), - Self::U32Lt => f.write_str("u32lt"), - Self::U32Lte => f.write_str("u32lte"), - Self::U32Gt => f.write_str("u32gt"), - Self::U32Gte => f.write_str("u32gte"), - Self::U32Min => f.write_str("u32min"), - Self::U32Max => f.write_str("u32max"), - } - } -} diff --git a/hir/src/asm/mod.rs b/hir/src/asm/mod.rs deleted file mode 100644 index 6b218f27d..000000000 --- a/hir/src/asm/mod.rs +++ /dev/null @@ -1,92 +0,0 @@ -mod builder; -mod display; -mod import; -mod isa; -mod stack; - -pub use self::builder::*; -pub use self::display::{DisplayInlineAsm, DisplayMasmBlock}; -pub use self::import::{MasmImport, ModuleImportInfo}; -pub use self::isa::*; -pub use self::stack::{OperandStack, Stack, StackElement}; - -use cranelift_entity::PrimaryMap; -use smallvec::smallvec; - -use super::{DataFlowGraph, Opcode, Type, ValueList}; - -/// Represents Miden Assembly (MASM) directly in the IR -/// -/// Each block of inline assembly executes in its own pseudo-isolated environment, -/// i.e. other than arguments provided to the inline assembly, and values introduced -/// within the inline assembly, it is not permitted to access anything else on the -/// operand stack. -/// -/// In addition to arguments, inline assembly can produce zero or more results, -/// see [MasmBuilder] for more info. -/// -/// Inline assembly can be built using [InstBuilder::inline_asm]. -#[derive(Debug, Clone)] -pub struct InlineAsm { - pub op: Opcode, - /// Arguments on which the inline assembly can operate - /// - /// The operand stack will be set up such that the given arguments - /// will appear in LIFO order, i.e. the first argument will be on top - /// of the stack, and so on. - /// - /// The inline assembly will be validated so that all other values on - /// the operand stack below the given arguments will remain on the stack - /// when the inline assembly finishes executing. - pub args: ValueList, - /// The types of the results produced by this inline assembly block - pub results: Vec, - /// The main code block - pub body: MasmBlockId, - /// The set of all code blocks contained in this inline assembly - /// - /// This is necessary to support control flow operations within asm blocks - pub blocks: PrimaryMap, -} -impl InlineAsm { - /// Constructs a new, empty inline assembly block with the given result type(s). - pub fn new(results: Vec) -> Self { - let mut blocks = PrimaryMap::::new(); - let id = blocks.next_key(); - let body = blocks.push(MasmBlock { - id, - ops: smallvec![], - }); - Self { - op: Opcode::InlineAsm, - args: ValueList::default(), - results, - body, - blocks, - } - } - - /// Create a new code block for use with this inline assembly - pub fn create_block(&mut self) -> MasmBlockId { - let id = self.blocks.next_key(); - self.blocks.push(MasmBlock { - id, - ops: smallvec![], - }); - id - } - - /// Appends `op` to the end of `block` - pub fn push(&mut self, block: MasmBlockId, op: MasmOp) { - self.blocks[block].push(op); - } - - pub fn display<'a, 'b: 'a>( - &'b self, - function: Option, - dfg: &'b DataFlowGraph, - indent: usize, - ) -> DisplayInlineAsm<'a> { - DisplayInlineAsm::new(function, self, dfg, indent) - } -} diff --git a/hir/src/asm/stack.rs b/hir/src/asm/stack.rs deleted file mode 100644 index 6de90ffd5..000000000 --- a/hir/src/asm/stack.rs +++ /dev/null @@ -1,638 +0,0 @@ -use std::{ - fmt, - ops::{Index, IndexMut}, -}; - -use crate::{Felt, FieldElement, StarkField, Type}; - -/// This trait is used to represent the basic plumbing of the operand stack in -/// Miden Assembly. -/// -/// Implementations of this trait may attach different semantics to the meaning of -/// elements on the stack. As a result, certain operations which are contingent on the -/// specific value of an element, may behave differently depending on the specific -/// implementation. -/// -/// In general however, it is expected that use of this trait in a generic context will -/// be rare, if ever the case. As mentioned above, it is meant to handle the common -/// plumbing of an operand stack implementation, but in practice users will be working -/// with a concrete implementation with this trait in scope to provide access to the -/// basic functionality of the stack. -/// -/// It is expected that implementations will override functions in this trait as necessary -/// to implement custom behavior above and beyond what is provided by the default implementation. -pub trait Stack: IndexMut::Element> { - type Element: StackElement; - - /// Return a reference to the underlying "raw" stack data structure, a vector - fn stack(&self) -> &Vec; - /// Return a mutable reference to the underlying "raw" stack data structure, a vector - fn stack_mut(&mut self) -> &mut Vec; - - /// Clear the contents of this stack while keeping the underlying - /// memory allocated for reuse. - fn clear(&mut self); - - /// Display this stack using its debugging representation - fn debug(&self) -> DebugStack { - DebugStack(self) - } - - /// Returns true if the operand stack is empty - #[inline(always)] - fn is_empty(&self) -> bool { - self.stack().is_empty() - } - - /// Returns the number of elements on the stack - #[inline] - fn len(&self) -> usize { - self.stack().len() - } - - /// Returns the value on top of the stack, without consuming it - #[inline] - fn peek(&self) -> Option { - self.stack().last().cloned() - } - - /// Returns the word on top of the stack, without consuming it - /// - /// The top of the stack will be the first element of the word - #[inline] - fn peekw(&self) -> Option<[Self::Element; 4]> { - let stack = self.stack(); - let end = stack.len().checked_sub(1)?; - Some([ - stack[end].clone(), - stack[end - 1].clone(), - stack[end - 2].clone(), - stack[end - 3].clone(), - ]) - } - - /// Pushes a word of zeroes on top of the stack - fn padw(&mut self) { - self.stack_mut().extend([ - Self::Element::DEFAULT, - Self::Element::DEFAULT, - Self::Element::DEFAULT, - Self::Element::DEFAULT, - ]); - } - - /// Pushes `value` on top of the stac - fn push(&mut self, value: Self::Element) { - self.stack_mut().push(value); - } - - /// Pushes `word` on top of the stack - /// - /// The first element of `word` will be on top of the stack - fn pushw(&mut self, mut word: [Self::Element; 4]) { - word.reverse(); - self.stack_mut().extend(word); - } - - /// Pops the value on top of the stack - fn pop(&mut self) -> Option { - self.stack_mut().pop() - } - - /// Pops the first word on top of the stack - /// - /// The top of the stack will be the first element in the result - fn popw(&mut self) -> Option<[Self::Element; 4]> { - let stack = self.stack_mut(); - let a = stack.pop()?; - let b = stack.pop()?; - let c = stack.pop()?; - let d = stack.pop()?; - Some([a, b, c, d]) - } - - /// Drops the top item on the stack - fn drop(&mut self) { - self.dropn(1); - } - - /// Drops the top word on the stack - fn dropw(&mut self) { - self.dropn(4); - } - - #[inline] - fn dropn(&mut self, n: usize) { - let stack = self.stack_mut(); - let len = stack.len(); - assert!( - n <= len, - "unable to drop {} elements, operand stack only has {}", - n, - len - ); - stack.truncate(len - n); - } - - /// Duplicates the value in the `n`th position on the stack - /// - /// If `n` is 0, duplicates the top of the stack. - fn dup(&mut self, n: usize) { - let value = self[n].clone(); - self.stack_mut().push(value); - } - - /// Duplicates the `n`th word on the stack, to the top of the stack. - /// - /// Valid values for `n` are 0, 1, 2, or 3. - /// - /// If `n` is 0, duplicates the top word of the stack. - fn dupw(&mut self, n: usize) { - assert!(n < 4, "invalid word index: must be in the range 0..=3"); - let len = self.stack().len(); - let index = n * 4; - assert!( - index < len, - "invalid operand stack index ({}), only {} elements are available", - index, - len - ); - match index { - 0 => { - let word = self.peekw().expect("operand stack is empty"); - self.pushw(word); - } - n => { - let end = len - n - 1; - let word = { - let stack = self.stack(); - [ - stack[end].clone(), - stack[end - 1].clone(), - stack[end - 2].clone(), - stack[end - 3].clone(), - ] - }; - self.pushw(word); - } - } - } - - /// Swaps the `n`th value from the top of the stack, with the top of the stack - /// - /// If `n` is 1, it swaps the first two elements on the stack. - /// - /// NOTE: This function will panic if `n` is 0, or out of bounds. - fn swap(&mut self, n: usize) { - assert_ne!(n, 0, "invalid swap, index must be in the range 1..=15"); - let stack = self.stack_mut(); - let len = stack.len(); - assert!( - n < len, - "invalid operand stack index ({}), only {} elements are available", - n, - len - ); - let a = len - 1; - let b = a - n; - stack.swap(a, b); - } - - /// Swaps the `n`th word from the top of the stack, with the word on top of the stack - /// - /// If `n` is 1, it swaps the first two words on the stack. - /// - /// Valid values for `n` are: 1, 2, 3. - fn swapw(&mut self, n: usize) { - assert_ne!(n, 0, "invalid swap, index must be in the range 1..=3"); - let stack = self.stack_mut(); - let len = stack.len(); - let index = n * 4; - assert!( - index < len, - "invalid operand stack index ({}), only {} elements are available", - index, - len - ); - let end = len - 1; - for offset in 0..4 { - // The index of the element in the top word - let a = end - offset; - // The index of the element in the `n`th word - let b = end - offset - index; - stack.swap(a, b); - } - } - - /// Moves the `n`th value to the top of the stack - /// - /// If `n` is 1, this is equivalent to `swap(1)`. - /// - /// NOTE: This function will panic if `n` is 0, or out of bounds. - fn movup(&mut self, n: usize) { - assert_ne!(n, 0, "invalid move, index must be in the range 1..=15"); - let stack = self.stack_mut(); - let len = stack.len(); - assert!( - n < len, - "invalid operand stack index ({}), only {} elements are available", - n, - len - ); - // Pick the midpoint by counting backwards from the end - let mid = len - (n + 1); - // Split the stack, and rotate the half that - // contains our desired value to place it on top. - let (_, r) = stack.split_at_mut(mid); - r.rotate_left(1); - } - - /// Moves the `n`th word to the top of the stack - /// - /// If `n` is 1, this is equivalent to `swapw(1)`. - /// - /// Valid values for `n` are: 1, 2, 3 - fn movupw(&mut self, n: usize) { - assert_ne!(n, 0, "invalid move, index must be in the range 1..=3"); - let stack = self.stack_mut(); - let len = stack.len(); - let index = (n * 4) + 4; - assert!( - index < len, - "invalid operand stack index ({}), only {} elements are available", - index, - len - ); - // Pick the midpoint index by counting backwards from the end - let mid = len - index; - // Split the stack, and rotate the half that - // contains our desired word to place it on top. - let (_, r) = stack.split_at_mut(mid); - r.rotate_left(4); - } - - /// Makes the value on top of the stack, the `n`th value on the stack - /// - /// If `n` is 1, this is equivalent to `swap(1)`. - /// - /// NOTE: This function will panic if `n` is 0, or out of bounds. - fn movdn(&mut self, n: usize) { - assert_ne!(n, 0, "invalid move: index must be in the range 1..=15"); - let stack = self.stack_mut(); - let len = stack.len(); - assert!( - n < len, - "invalid operand stack index ({}), only {} elements are available", - n, - len - ); - // Split the stack so that the desired position is in the top half - let mid = len - (n + 1); - let (_, r) = stack.split_at_mut(mid); - // Move all elements above the `n`th position up by one, moving the top element to the `n`th position - r.rotate_right(1); - } - - /// Makes the word on top of the stack, the `n`th word on the stack - /// - /// If `n` is 1, this is equivalent to `swapw(1)`. - /// - /// Valid values for `n` are: 1, 2, 3 - fn movdnw(&mut self, n: usize) { - assert_ne!(n, 0, "invalid move, index must be in the range 1..=3"); - let stack = self.stack_mut(); - let len = stack.len(); - let index = (n * 4) + 4; - assert!( - index < len, - "invalid operand stack index ({}), only {} elements are available", - index, - len - ); - // Split the stack so that the desired position is in the top half - let mid = len - index; - let (_, r) = stack.split_at_mut(mid); - // Move all elements above the `n`th word up by one word, moving the top word to the `n`th position - r.rotate_right(4); - } -} - -/// This trait is used to represent expected behavior/properties of elements -/// that can be used in conjunction with the [Stack] trait. -pub trait StackElement: Clone { - type Debug: fmt::Debug; - - /// A value of this type which represents the "zero" value for the type - const DEFAULT: Self; - - /// Format this stack element for display - fn debug(&self) -> Self::Debug; -} - -impl StackElement for Felt { - type Debug = u64; - - const DEFAULT: Self = Felt::ZERO; - - #[inline] - fn debug(&self) -> Self::Debug { - self.as_int() - } -} -impl StackElement for Type { - type Debug = Type; - - const DEFAULT: Self = Type::Felt; - - #[inline] - fn debug(&self) -> Self::Debug { - self.clone() - } -} - -/// This structure is a concrete implementation of the [Stack] trait, implemented -/// for use with two different element types: -/// -/// * [Felt], for actual emulation of the Miden VM operand stack -/// * [Type], for tracking the state of the operand stack in abstract -pub struct OperandStack { - stack: Vec, -} -impl Clone for OperandStack { - fn clone(&self) -> Self { - Self { - stack: self.stack.clone(), - } - } -} -impl Default for OperandStack { - fn default() -> Self { - Self { stack: vec![] } - } -} -impl Stack for OperandStack { - type Element = T; - - #[inline(always)] - fn stack(&self) -> &Vec { - &self.stack - } - #[inline(always)] - fn stack_mut(&mut self) -> &mut Vec { - &mut self.stack - } - #[inline(always)] - fn clear(&mut self) { - self.stack.clear(); - } -} -impl OperandStack { - /// Pushes `value` on top of the stack, with an optional set of aliases - pub fn push_u8(&mut self, value: u8) { - self.stack.push(Felt::new(value as u64)); - } - - /// Pushes `value` on top of the stack, with an optional set of aliases - pub fn push_u16(&mut self, value: u16) { - self.stack.push(Felt::new(value as u64)); - } - - /// Pushes `value` on top of the stack, with an optional set of aliases - pub fn push_u32(&mut self, value: u32) { - self.stack.push(Felt::new(value as u64)); - } -} -impl Index for OperandStack { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - let len = self.stack.len(); - assert!( - index < 16, - "invalid operand stack index ({}), only the top 16 elements are directly accessible", - index - ); - assert!( - index < len, - "invalid operand stack index ({}), only {} elements are available", - index, - len - ); - &self.stack[len - index - 1] - } -} -impl IndexMut for OperandStack { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - let len = self.stack.len(); - assert!( - index < 16, - "invalid operand stack index ({}), only the top 16 elements are directly accessible", - index - ); - assert!( - index < len, - "invalid operand stack index ({}), only {} elements are available", - index, - len - ); - &mut self.stack[len - index - 1] - } -} - -#[doc(hidden)] -pub struct DebugStack<'a, T: ?Sized + Stack>(&'a T); -impl<'a, E: StackElement, T: ?Sized + Stack> fmt::Debug for DebugStack<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - #[allow(unused)] - struct StackEntry<'a, E: StackElement> { - index: usize, - value: &'a E, - } - impl<'a, E: StackElement> fmt::Debug for StackEntry<'a, E> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("StackEntry") - .field("index", &self.index) - .field("value", &self.value.debug()) - .finish() - } - } - - f.debug_list() - .entries( - self.0 - .stack() - .iter() - .rev() - .enumerate() - .map(|(index, value)| StackEntry { index, value }), - ) - .finish() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Felt; - - #[test] - fn operand_stack_primitive_ops_test() { - let mut stack = OperandStack::::default(); - - let zero = Felt::new(0); - let one = Felt::new(1); - let two = Felt::new(2); - let three = Felt::new(3); - let four = Felt::new(4); - let five = Felt::new(5); - let six = Felt::new(6); - let seven = Felt::new(7); - - // push - stack.push(zero); - stack.push(one); - stack.push(two); - stack.push(three); - assert_eq!(stack.len(), 4); - assert_eq!(stack[0].as_int(), 3); - assert_eq!(stack[1].as_int(), 2); - assert_eq!(stack[2].as_int(), 1); - assert_eq!(stack[3].as_int(), 0); - - #[inline(always)] - fn as_int(word: [Felt; 4]) -> [u64; 4] { - [ - word[0].as_int(), - word[1].as_int(), - word[2].as_int(), - word[3].as_int(), - ] - } - - // peekw - assert_eq!(stack.peekw().map(as_int), Some([3, 2, 1, 0])); - - // dupw - stack.dupw(0); - assert_eq!(stack.len(), 8); - assert_eq!(stack.peekw().map(as_int), Some([3, 2, 1, 0])); - - // padw - stack.padw(); - assert_eq!(stack.len(), 12); - assert_eq!(stack.peekw().map(as_int), Some([0; 4])); - - // swapw - stack.swapw(1); - assert_eq!(stack.len(), 12); - assert_eq!(stack.peekw().map(as_int), Some([3, 2, 1, 0])); - - // popw - let word = stack.popw(); - assert_eq!(stack.len(), 8); - assert_eq!(word.map(as_int), Some([3, 2, 1, 0])); - - // pushw - stack.pushw(word.unwrap()); - stack.pushw([seven, six, five, four]); - assert_eq!(stack.len(), 16); - assert_eq!(stack.peekw().map(as_int), Some([7, 6, 5, 4])); - - // movupw - stack.movupw(2); - assert_eq!(stack.len(), 16); - assert_eq!(stack.peekw().map(as_int), Some([0; 4])); - assert_eq!(stack[8].as_int(), 3); - assert_eq!(stack[9].as_int(), 2); - assert_eq!(stack[10].as_int(), 1); - assert_eq!(stack[11].as_int(), 0); - - // movdnw - stack.movdnw(2); - assert_eq!(stack.len(), 16); - assert_eq!(stack.peekw().map(as_int), Some([7, 6, 5, 4])); - assert_eq!(stack[8].as_int(), 0); - assert_eq!(stack[9].as_int(), 0); - assert_eq!(stack[10].as_int(), 0); - assert_eq!(stack[11].as_int(), 0); - - // dropw - stack.movupw(2); - stack.dropw(); - assert_eq!(stack.len(), 12); - assert_eq!(stack.peekw().map(as_int), Some([7, 6, 5, 4])); - - // dup(n) - stack.dup(0); - assert_eq!(stack.len(), 13); - assert_eq!(stack[0].as_int(), 7); - assert_eq!(stack[1].as_int(), 7); - assert_eq!(stack[2].as_int(), 6); - assert_eq!(stack[3].as_int(), 5); - assert_eq!(stack[4].as_int(), 4); - stack.dup(3); - assert_eq!(stack.len(), 14); - assert_eq!(stack[0].as_int(), 5); - assert_eq!(stack[1].as_int(), 7); - assert_eq!(stack[2].as_int(), 7); - assert_eq!(stack[3].as_int(), 6); - assert_eq!(stack[4].as_int(), 5); - assert_eq!(stack[5].as_int(), 4); - - // swap(n) - stack.swap(1); - assert_eq!(stack.len(), 14); - assert_eq!(stack[0].as_int(), 7); - assert_eq!(stack[1].as_int(), 5); - assert_eq!(stack[2].as_int(), 7); - assert_eq!(stack[3].as_int(), 6); - assert_eq!(stack[4].as_int(), 5); - assert_eq!(stack[5].as_int(), 4); - - // movup(n) - stack.movup(3); - assert_eq!(stack.len(), 14); - assert_eq!(stack[0].as_int(), 6); - assert_eq!(stack[1].as_int(), 7); - assert_eq!(stack[2].as_int(), 5); - assert_eq!(stack[3].as_int(), 7); - assert_eq!(stack[4].as_int(), 5); - assert_eq!(stack[5].as_int(), 4); - - // movdn(n) - stack.movdn(3); - assert_eq!(stack.len(), 14); - assert_eq!(stack[0].as_int(), 7); - assert_eq!(stack[1].as_int(), 5); - assert_eq!(stack[2].as_int(), 7); - assert_eq!(stack[3].as_int(), 6); - assert_eq!(stack[4].as_int(), 5); - assert_eq!(stack[5].as_int(), 4); - - // drop - stack.drop(); - assert_eq!(stack.len(), 13); - assert_eq!(stack[0].as_int(), 5); - assert_eq!(stack[1].as_int(), 7); - assert_eq!(stack[2].as_int(), 6); - assert_eq!(stack[3].as_int(), 5); - assert_eq!(stack[4].as_int(), 4); - - // dropn - stack.dropn(2); - assert_eq!(stack.len(), 11); - assert_eq!(stack[0].as_int(), 6); - assert_eq!(stack[1].as_int(), 5); - assert_eq!(stack[2].as_int(), 4); - - // push - stack.push(six); - stack.push(seven); - assert_eq!(stack.len(), 13); - assert_eq!(stack[0].as_int(), 7); - assert_eq!(stack[1].as_int(), 6); - assert_eq!(stack[2].as_int(), 6); - assert_eq!(stack[3].as_int(), 5); - assert_eq!(stack[4].as_int(), 4); - } -} diff --git a/hir/src/attribute.rs b/hir/src/attribute.rs deleted file mode 100644 index 122f22247..000000000 --- a/hir/src/attribute.rs +++ /dev/null @@ -1,271 +0,0 @@ -use std::{borrow::Borrow, collections::BTreeMap, fmt}; - -use crate::Symbol; - -pub mod attributes { - use super::*; - use crate::symbols; - - /// This attribute indicates that the decorated function is the entrypoint - /// for its containing program, regardless of what module it is defined in. - pub const ENTRYPOINT: Attribute = Attribute { - name: symbols::Entrypoint, - value: AttributeValue::Unit, - }; -} - -/// An [AttributeSet] is a uniqued collection of attributes associated with some IR entity -#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] -pub struct AttributeSet(BTreeMap); -impl FromIterator for AttributeSet { - fn from_iter(attrs: T) -> Self - where - T: IntoIterator, - { - let mut map = BTreeMap::default(); - for attr in attrs.into_iter() { - map.insert(attr.name, attr.value); - } - Self(map) - } -} -impl FromIterator<(Symbol, AttributeValue)> for AttributeSet { - fn from_iter(attrs: T) -> Self - where - T: IntoIterator, - { - let mut map = BTreeMap::default(); - for (name, value) in attrs.into_iter() { - map.insert(name, value); - } - Self(map) - } -} -impl AttributeSet { - /// Get a new, empty [AttributeSet] - pub fn new() -> Self { - Self::default() - } - - /// Insert a new [Attribute] in this set by `name` and `value` - pub fn insert(&mut self, name: impl Into, value: impl Into) { - self.0.insert(name.into(), value.into()); - } - - /// Adds `attr` to this set - pub fn set(&mut self, attr: Attribute) { - self.0.insert(attr.name, attr.value); - } - - /// Remove an [Attribute] by name from this set - pub fn remove(&mut self, name: &Q) - where - Symbol: Borrow, - Q: Ord + ?Sized, - { - self.0.remove(name); - } - - /// Determine if the named [Attribute] is present in this set - pub fn has(&self, key: &Q) -> bool - where - Symbol: Borrow, - Q: Ord + ?Sized, - { - self.0.contains_key(key) - } - - /// Get the [AttributeValue] associated with the named [Attribute] - pub fn get(&self, key: &Q) -> Option<&AttributeValue> - where - Symbol: Borrow, - Q: Ord + ?Sized, - { - self.0.get(key) - } - - /// Get the value associated with the named [Attribute] as a boolean, or `None`. - pub fn get_bool(&self, key: &Q) -> Option - where - Symbol: Borrow, - Q: Ord + ?Sized, - { - self.0.get(key).and_then(|v| v.as_bool()) - } - - /// Get the value associated with the named [Attribute] as an integer, or `None`. - pub fn get_int(&self, key: &Q) -> Option - where - Symbol: Borrow, - Q: Ord + ?Sized, - { - self.0.get(key).and_then(|v| v.as_int()) - } - - /// Get the value associated with the named [Attribute] as a [Symbol], or `None`. - pub fn get_symbol(&self, key: &Q) -> Option - where - Symbol: Borrow, - Q: Ord + ?Sized, - { - self.0.get(key).and_then(|v| v.as_symbol()) - } - - /// Iterate over each [Attribute] in this set - pub fn iter(&self) -> impl Iterator + '_ { - self.0.iter().map(|(k, v)| Attribute { - name: *k, - value: *v, - }) - } -} - -/// An [Attribute] associates some data with a well-known identifier (name). -/// -/// Attributes are used for representing metadata that helps guide compilation, -/// but which is not part of the code itself. For example, `cfg` flags in Rust -/// are an example of something which you could represent using an [Attribute]. -/// They can also be used to store documentation, source locations, and more. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Attribute { - /// The name of this attribute - pub name: Symbol, - /// The value associated with this attribute - pub value: AttributeValue, -} -impl Attribute { - pub fn new(name: impl Into, value: impl Into) -> Self { - Self { - name: name.into(), - value: value.into(), - } - } -} -impl fmt::Display for Attribute { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.value { - AttributeValue::Unit => write!(f, "#[{}]", self.name.as_str()), - value => write!(f, "#[{}({value})]", &self.name), - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum AttributeValue { - /// No concrete value (i.e. presence of the attribute is significant) - Unit, - /// A boolean value - Bool(bool), - /// A signed integer - Int(isize), - /// An interned string - String(Symbol), -} -impl AttributeValue { - pub fn as_bool(&self) -> Option { - match self { - Self::Bool(value) => Some(*value), - _ => None, - } - } - - pub fn as_int(&self) -> Option { - match self { - Self::Int(value) => Some(*value), - _ => None, - } - } - - pub fn as_symbol(&self) -> Option { - match self { - Self::String(value) => Some(*value), - _ => None, - } - } -} -impl fmt::Display for AttributeValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Unit => f.write_str("()"), - Self::Bool(value) => write!(f, "{value}"), - Self::Int(value) => write!(f, "{value}"), - Self::String(value) => write!(f, "\"{}\"", value.as_str().escape_default()), - } - } -} -impl From<()> for AttributeValue { - fn from(_: ()) -> Self { - Self::Unit - } -} -impl From for AttributeValue { - fn from(value: bool) -> Self { - Self::Bool(value) - } -} -impl From for AttributeValue { - fn from(value: isize) -> Self { - Self::Int(value) - } -} -impl From<&str> for AttributeValue { - fn from(value: &str) -> Self { - Self::String(Symbol::intern(value)) - } -} -impl From for AttributeValue { - fn from(value: String) -> Self { - Self::String(Symbol::intern(value.as_str())) - } -} -impl From for AttributeValue { - fn from(value: u8) -> Self { - Self::Int(value as isize) - } -} -impl From for AttributeValue { - fn from(value: i8) -> Self { - Self::Int(value as isize) - } -} -impl From for AttributeValue { - fn from(value: u16) -> Self { - Self::Int(value as isize) - } -} -impl From for AttributeValue { - fn from(value: i16) -> Self { - Self::Int(value as isize) - } -} -impl From for AttributeValue { - fn from(value: u32) -> Self { - Self::Int(value as isize) - } -} -impl From for AttributeValue { - fn from(value: i32) -> Self { - Self::Int(value as isize) - } -} -impl TryFrom for AttributeValue { - type Error = core::num::TryFromIntError; - - fn try_from(value: usize) -> Result { - Ok(Self::Int(value.try_into()?)) - } -} -impl TryFrom for AttributeValue { - type Error = core::num::TryFromIntError; - - fn try_from(value: u64) -> Result { - Ok(Self::Int(value.try_into()?)) - } -} -impl TryFrom for AttributeValue { - type Error = core::num::TryFromIntError; - - fn try_from(value: i64) -> Result { - Ok(Self::Int(value.try_into()?)) - } -} diff --git a/hir/src/block.rs b/hir/src/block.rs deleted file mode 100644 index f7ef8c857..000000000 --- a/hir/src/block.rs +++ /dev/null @@ -1,134 +0,0 @@ -use cranelift_entity::entity_impl; -use intrusive_collections::linked_list::{Cursor, CursorMut}; -use intrusive_collections::UnsafeRef; - -use super::*; - -/// A handle to a single function block -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Block(u32); -entity_impl!(Block, "block"); -impl Default for Block { - #[inline] - fn default() -> Self { - use cranelift_entity::packed_option::ReservedValue; - - Self::reserved_value() - } -} - -/// Data associated with a `Block`. -/// -/// Blocks have arguments, and consist of a sequence of instructions. -pub struct BlockData { - pub id: Block, - pub params: ValueList, - pub insts: InstructionList, -} -impl Drop for BlockData { - fn drop(&mut self) { - self.insts.fast_clear(); - } -} -impl Clone for BlockData { - fn clone(&self) -> Self { - Self { - id: self.id, - params: self.params, - insts: Default::default(), - } - } -} -impl BlockData { - pub(crate) fn new(id: Block) -> Self { - Self { - id, - params: ValueList::new(), - insts: Default::default(), - } - } - - #[inline] - pub fn arity(&self, pool: &ValueListPool) -> usize { - self.params.len(pool) - } - - #[inline] - pub fn params<'a, 'b: 'a>(&'b self, pool: &'a ValueListPool) -> &'a [Value] { - self.params.as_slice(pool) - } - - pub fn insts(&self) -> impl Iterator + '_ { - Insts { - cursor: self.insts.front(), - } - } - - #[inline(always)] - pub fn prepend(&mut self, inst: UnsafeRef) { - self.insts.push_front(inst); - } - - #[inline(always)] - pub fn append(&mut self, inst: UnsafeRef) { - self.insts.push_back(inst); - } - - #[inline(always)] - pub fn cursor_mut<'a, 'b: 'a>(&'b mut self) -> CursorMut<'a, InstAdapter> { - self.insts.front_mut() - } - - pub fn cursor_mut_at<'a, 'b: 'a>(&'b mut self, index: usize) -> CursorMut<'a, InstAdapter> { - let mut cursor = self.insts.front_mut(); - for _ in 0..index { - cursor.move_next(); - assert!(!cursor.is_null(), "index out of bounds"); - } - cursor - } - - #[inline] - pub fn insert_after(&mut self, index: usize, inst: UnsafeRef) { - let mut cursor = self.cursor_mut_at(index); - cursor.insert_after(inst); - } - - #[inline] - pub fn insert_before(&mut self, index: usize, inst: UnsafeRef) { - let mut cursor = self.cursor_mut_at(index); - cursor.insert_before(inst); - } - - pub fn first(&self) -> Option { - self.insts.front().get().map(|data| data.key) - } - - pub fn last(&self) -> Option { - self.insts.back().get().map(|data| data.key) - } - - pub fn is_empty(&self) -> bool { - self.insts.is_empty() - } - - pub fn len(&self) -> usize { - self.insts.iter().count() - } -} - -struct Insts<'f> { - cursor: Cursor<'f, InstAdapter>, -} -impl<'f> Iterator for Insts<'f> { - type Item = Inst; - - fn next(&mut self) -> Option { - if self.cursor.is_null() { - return None; - } - let next = self.cursor.get().map(|data| data.key); - self.cursor.move_next(); - next - } -} diff --git a/hir/src/builder.rs b/hir/src/builder.rs deleted file mode 100644 index 6436ec837..000000000 --- a/hir/src/builder.rs +++ /dev/null @@ -1,1556 +0,0 @@ -use cranelift_entity::packed_option::PackedOption; - -use miden_diagnostics::SourceSpan; - -use super::*; - -pub struct FunctionBuilder<'f> { - pub func: &'f mut Function, - position: PackedOption, -} -impl<'f> FunctionBuilder<'f> { - pub fn new(func: &'f mut Function) -> Self { - let entry = func.dfg.entry_block(); - let position = func.dfg.last_block().unwrap_or(entry); - - Self { - func, - position: position.into(), - } - } - - pub fn entry_block(&self) -> Block { - self.func.dfg.entry_block() - } - - #[inline] - pub fn current_block(&self) -> Block { - self.position.expand().unwrap() - } - - #[inline] - pub fn switch_to_block(&mut self, block: Block) { - self.position = PackedOption::from(block); - } - - pub fn create_block(&mut self) -> Block { - self.func.dfg.create_block() - } - - pub fn detach_block(&mut self, block: Block) { - assert_ne!( - block, - self.current_block(), - "cannot remove block the builder is currently inserting in" - ); - self.func.dfg.detach_block(block); - } - - pub fn block_params(&self, block: Block) -> &[Value] { - self.func.dfg.block_params(block) - } - - pub fn append_block_param(&mut self, block: Block, ty: Type, span: SourceSpan) -> Value { - self.func.dfg.append_block_param(block, ty, span) - } - - pub fn inst_results(&self, inst: Inst) -> &[Value] { - self.func.dfg.inst_results(inst) - } - - pub fn first_result(&self, inst: Inst) -> Value { - self.func.dfg.first_result(inst) - } - - pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue { - self.func.dfg.create_global_value(data) - } - - pub fn get_import(&self, id: &FunctionIdent) -> Option<&ExternalFunction> { - self.func.dfg.get_import(id) - } - - #[inline] - pub fn import_function, F: AsRef>( - &mut self, - module: M, - function: F, - signature: Signature, - span: SourceSpan, - ) -> Result { - let module = Ident::with_empty_span(Symbol::intern(module.as_ref())); - let function = Ident::new(Symbol::intern(function.as_ref()), span); - self.func.dfg.import_function(module, function, signature) - } - - pub fn ins<'a, 'b: 'a>(&'b mut self) -> DefaultInstBuilder<'a> { - let block = self - .position - .expect("must be in a block to insert instructions"); - DefaultInstBuilder::new(&mut self.func.dfg, block) - } -} - -pub struct DefaultInstBuilder<'f> { - dfg: &'f mut DataFlowGraph, - ip: InsertionPoint, -} -impl<'f> DefaultInstBuilder<'f> { - pub(crate) fn new(dfg: &'f mut DataFlowGraph, block: Block) -> Self { - assert!(dfg.is_block_linked(block)); - - Self { - dfg, - ip: InsertionPoint::after(ProgramPoint::Block(block)), - } - } - - fn at(dfg: &'f mut DataFlowGraph, ip: InsertionPoint) -> Self { - Self { dfg, ip } - } -} -impl<'f> InstBuilderBase<'f> for DefaultInstBuilder<'f> { - fn data_flow_graph(&self) -> &DataFlowGraph { - self.dfg - } - - fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { - self.dfg - } - - fn insertion_point(&self) -> InsertionPoint { - self.ip - } - - fn build(self, data: Instruction, ty: Type, span: SourceSpan) -> (Inst, &'f mut DataFlowGraph) { - if cfg!(debug_assertions) { - if let InsertionPoint { - at: ProgramPoint::Block(blk), - action: Insert::After, - } = self.ip - { - debug_assert!( - !self.dfg.is_block_terminated(blk), - "cannot append an instruction to a block that is already terminated" - ); - } - } - - let inst = self.dfg.insert_inst(self.ip, data, ty, span); - (inst, self.dfg) - } -} - -/// Instruction builder that replaces an existing instruction. -/// -/// The inserted instruction will have the same `Inst` number as the old one. -/// -/// If the old instruction still has result values attached, it is assumed that the new instruction -/// produces the same number and types of results. The old result values are preserved. If the -/// replacement instruction format does not support multiple results, the builder panics. It is a -/// bug to leave result values dangling. -pub struct ReplaceBuilder<'f> { - dfg: &'f mut DataFlowGraph, - inst: Inst, -} - -impl<'f> ReplaceBuilder<'f> { - /// Create a `ReplaceBuilder` that will overwrite `inst`. - pub fn new(dfg: &'f mut DataFlowGraph, inst: Inst) -> Self { - Self { dfg, inst } - } -} - -impl<'f> InstBuilderBase<'f> for ReplaceBuilder<'f> { - fn data_flow_graph(&self) -> &DataFlowGraph { - self.dfg - } - - fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { - self.dfg - } - - fn insertion_point(&self) -> InsertionPoint { - InsertionPoint::before(ProgramPoint::Inst(self.inst)) - } - - fn build( - self, - data: Instruction, - 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. - // Construct new ones. - self.dfg.replace_results(self.inst, ctrl_typevar); - - (self.inst, self.dfg) - } -} - -pub trait InstBuilderBase<'f>: Sized { - /// Get a reference to the underlying [DataFlowGraph] for this builder - fn data_flow_graph(&self) -> &DataFlowGraph; - /// Get a mutable reference to the underlying [DataFlowGraph] for this builder - fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph; - /// Return the insertion point of this builder - fn insertion_point(&self) -> InsertionPoint; - /// Build the given instruction, returing it's handle and the inner [DataFlowGraph] - fn build( - self, - data: Instruction, - ctrl_ty: Type, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph); - /// Get a default instruction builder using the dataflow graph and insertion point of the current builder - fn ins<'a, 'b: 'a>(&'b mut self) -> DefaultInstBuilder<'a> { - let ip = self.insertion_point(); - DefaultInstBuilder::at(self.data_flow_graph_mut(), ip) - } -} - -macro_rules! into_first_result { - ($built:expr) => {{ - let (inst, dfg) = $built; - dfg.first_result(inst) - }}; -} - -macro_rules! assert_integer_operands { - ($this:ident, $lhs:ident, $rhs:ident) => {{ - let lty = require_matching_operands!($this, $lhs, $rhs); - assert!( - lty.is_integer(), - "expected {} and {} to be of integral type", - stringify!($lhs), - stringify!($rhs) - ); - lty.clone() - }}; - - ($this:ident, $lhs:ident) => {{ - let ty = require_integer!($this, $lhs); - ty.clone() - }}; -} - -macro_rules! require_matching_operands { - ($this:ident, $lhs:ident, $rhs:ident) => {{ - let lty = $this.data_flow_graph().value_type($lhs); - let rty = $this.data_flow_graph().value_type($rhs); - assert_eq!( - lty, - rty, - "expected {} and {} to be of the same type", - stringify!($lhs), - stringify!($rhs) - ); - lty - }}; -} - -macro_rules! require_unsigned_integer { - ($this:ident, $val:ident) => {{ - let ty = $this.data_flow_graph().value_type($val); - assert!( - ty.is_unsigned_integer(), - "expected {} to be an unsigned integral type", - stringify!($val) - ); - ty - }}; -} - -macro_rules! require_integer { - ($this:ident, $val:ident) => {{ - let ty = $this.data_flow_graph().value_type($val); - assert!( - ty.is_integer(), - "expected {} to be of integral type", - stringify!($val) - ); - ty - }}; - - ($this:ident, $val:ident, $ty:expr) => {{ - let ty = $this.data_flow_graph().value_type($val); - let expected_ty = $ty; - assert!( - ty.is_integer(), - "expected {} to be of integral type", - stringify!($val) - ); - assert_eq!( - ty, - &expected_ty, - "expected {} to be a {}", - stringify!($val), - &expected_ty - ); - ty - }}; -} - -macro_rules! require_pointer { - ($this:ident, $val:ident) => {{ - let ty = $this.data_flow_graph().value_type($val); - assert!( - ty.is_pointer(), - "expected {} to be of pointer type, got {}", - stringify!($val), - &ty - ); - ty - }}; -} - -macro_rules! require_pointee { - ($this:ident, $val:ident) => {{ - let ty = $this.data_flow_graph().value_type($val); - let pointee_ty = ty.pointee(); - assert!( - pointee_ty.is_some(), - "expected {} to be of pointer type, got {}", - stringify!($val), - &ty - ); - pointee_ty.unwrap() - }}; -} - -macro_rules! binary_int_op { - ($name:ident, $op:expr) => { - paste::paste! { - fn $name(self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs, rhs); - into_first_result!(self.Binary($op, lty, lhs, rhs, span)) - } - fn [<$name _imm>](self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - into_first_result!(self.BinaryImm($op, lty, lhs, imm, span)) - } - } - }; -} - -macro_rules! binary_int_op_with_overflow { - ($name:ident, $op:expr) => { - paste::paste! { - fn [<$name _checked>](self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs, rhs); - into_first_result!(self.BinaryWithOverflow($op, lty, lhs, rhs, Overflow::Checked, span)) - } - fn [<$name _unchecked>](self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs, rhs); - into_first_result!(self.BinaryWithOverflow($op, lty, lhs, rhs, Overflow::Unchecked, span)) - } - fn [<$name _wrapping>](self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs, rhs); - into_first_result!(self.BinaryWithOverflow($op, lty, lhs, rhs, Overflow::Wrapping, span)) - } - fn [<$name _overflowing>](self, lhs: Value, rhs: Value, span: SourceSpan) -> Inst { - let lty = assert_integer_operands!(self, lhs, rhs); - self.BinaryWithOverflow($op, lty, lhs, rhs, Overflow::Overflowing, span).0 - } - fn [<$name _imm_checked>](self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - into_first_result!(self.BinaryImmWithOverflow($op, lty, lhs, imm, Overflow::Checked, span)) - } - fn [<$name _imm_unchecked>](self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - into_first_result!(self.BinaryImmWithOverflow($op, lty, lhs, imm, Overflow::Unchecked, span)) - } - fn [<$name _imm_wrapping>](self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - into_first_result!(self.BinaryImmWithOverflow($op, lty, lhs, imm, Overflow::Wrapping, span)) - } - fn [<$name _imm_overflowing>](self, lhs: Value, imm: Immediate, span: SourceSpan) -> Inst { - let lty = assert_integer_operands!(self, lhs); - self.BinaryImmWithOverflow($op, lty, lhs, imm, Overflow::Overflowing, span).0 - } - } - } -} - -macro_rules! checked_binary_int_op { - ($name:ident, $op:expr) => { - paste::paste! { - fn [<$name _checked>](self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs, rhs); - into_first_result!(self.BinaryWithOverflow($op, lty, lhs, rhs, Overflow::Checked, span)) - } - fn [<$name _unchecked>](self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs, rhs); - into_first_result!(self.BinaryWithOverflow($op, lty, lhs, rhs, Overflow::Unchecked, span)) - } - fn [<$name _imm_checked>](self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - into_first_result!(self.BinaryImmWithOverflow($op, lty, lhs, imm, Overflow::Checked, span)) - } - fn [<$name _imm_unchecked>](self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - into_first_result!(self.BinaryImmWithOverflow($op, lty, lhs, imm, Overflow::Unchecked, span)) - } - } - }; -} - -macro_rules! binary_boolean_op { - ($name:ident, $op:expr) => { - paste::paste! { - fn $name(self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - let lty = require_matching_operands!(self, lhs, rhs).clone(); - assert_eq!(lty, Type::I1, concat!(stringify!($name), " requires boolean operands")); - into_first_result!(self.Binary($op, lty, lhs, rhs, span)) - } - fn [<$name _imm>](self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = require_integer!(self, lhs, Type::I1).clone(); - assert_eq!(lty, Type::I1, concat!(stringify!($name), " requires boolean operands")); - into_first_result!(self.BinaryImm($op, lty, lhs, imm, span)) - } - } - }; -} - -macro_rules! unary_int_op { - ($name:ident, $op:expr) => { - fn $name(self, rhs: Value, span: SourceSpan) -> Value { - let rty = assert_integer_operands!(self, rhs); - into_first_result!(self.Unary($op, rty, rhs, span)) - } - }; -} - -macro_rules! unary_int_op_with_overflow { - ($name:ident, $op:expr) => { - paste::paste! { - fn [<$name _checked>](self, rhs: Value, span: SourceSpan) -> Value { - let rty = assert_integer_operands!(self, rhs); - into_first_result!(self.UnaryWithOverflow($op, rty, rhs, Overflow::Checked, span)) - } - fn [<$name _unchecked>](self, rhs: Value, span: SourceSpan) -> Value { - let rty = assert_integer_operands!(self, rhs); - into_first_result!(self.UnaryWithOverflow($op, rty, rhs, Overflow::Unchecked, span)) - } - fn [<$name _wrapping>](self, rhs: Value, span: SourceSpan) -> Value { - let rty = assert_integer_operands!(self, rhs); - into_first_result!(self.UnaryWithOverflow($op, rty, rhs, Overflow::Wrapping, span)) - } - fn [<$name _overflowing>](self, rhs: Value, span: SourceSpan) -> Inst { - let rty = assert_integer_operands!(self, rhs); - self.UnaryWithOverflow($op, rty, rhs, Overflow::Overflowing, span).0 - } - } - }; -} - -macro_rules! unary_boolean_op { - ($name:ident, $op:expr) => { - fn $name(self, rhs: Value, span: SourceSpan) -> Value { - let rty = require_integer!(self, rhs, Type::I1).clone(); - into_first_result!(self.Unary($op, rty, rhs, span)) - } - }; -} - -macro_rules! integer_literal { - ($width:literal) => { - paste::paste! { - unsigned_integer_literal!($width); - signed_integer_literal!($width); - } - }; - - ($width:ident) => { - paste::paste! { - unsigned_integer_literal!($width); - signed_integer_literal!($width); - } - }; - - ($width:literal, $ty:ty) => { - paste::paste! { - unsigned_integer_literal!($width, $ty); - signed_integer_literal!($width, $ty); - } - }; - - ($width:ident, $ty:ty) => { - paste::paste! { - unsigned_integer_literal!($width, $ty); - signed_integer_literal!($width, $ty); - } - }; -} - -macro_rules! unsigned_integer_literal { - ($width:literal) => { - paste::paste! { - unsigned_integer_literal!($width, []); - } - }; - - ($width:ident) => { - paste::paste! { - unsigned_integer_literal!($width, []); - } - }; - - ($width:literal, $ty:ty) => { - paste::paste! { - unsigned_integer_literal!([], [], $ty); - } - }; - - ($width:ident, $ty:ty) => { - paste::paste! { - unsigned_integer_literal!([], [], $ty); - } - }; - - ($name:ident, $suffix:ident, $ty:ty) => { - paste::paste! { - unsigned_integer_literal!($name, $suffix, $ty, []); - } - }; - - ($name:ident, $suffix:ident, $ty:ty, $op:ident) => { - paste::paste! { - fn $name(self, imm: $ty, span: SourceSpan) -> Value { - into_first_result!(self.UnaryImm(Opcode::$op, Type::$suffix, Immediate::$suffix(imm), span)) - } - } - }; -} - -macro_rules! signed_integer_literal { - ($width:literal) => { - paste::paste! { - signed_integer_literal!($width, []); - } - }; - - ($width:ident) => { - paste::paste! { - signed_integer_literal!($width, []); - } - }; - - ($width:literal, $ty:ty) => { - paste::paste! { - signed_integer_literal!([], [], $ty); - } - }; - - ($width:ident, $ty:ty) => { - paste::paste! { - signed_integer_literal!([], [], $ty); - } - }; - - ($name:ident, $suffix:ident, $ty:ty) => { - paste::paste! { - signed_integer_literal!($name, $suffix, $ty, []); - } - }; - - ($name:ident, $suffix:ident, $ty:ty, $op:ident) => { - paste::paste! { - fn $name(self, imm: $ty, span: SourceSpan) -> Value { - into_first_result!(self.UnaryImm(Opcode::$op, Type::$suffix, Immediate::$suffix(imm), span)) - } - } - }; -} - -pub trait InstBuilder<'f>: InstBuilderBase<'f> { - fn assert(mut self, value: Value, span: SourceSpan) -> Inst { - require_integer!(self, value, Type::I1); - let mut vlist = ValueList::default(); - { - let pool = &mut self.data_flow_graph_mut().value_lists; - vlist.push(value, pool); - } - self.PrimOp(Opcode::Assert, Type::Unit, vlist, span).0 - } - - fn assertz(mut self, value: Value, span: SourceSpan) -> Inst { - require_integer!(self, value, Type::I1); - let mut vlist = ValueList::default(); - { - let pool = &mut self.data_flow_graph_mut().value_lists; - vlist.push(value, pool); - } - self.PrimOp(Opcode::Assertz, Type::Unit, vlist, span).0 - } - - fn assert_eq(mut self, lhs: Value, rhs: Value, span: SourceSpan) -> Inst { - require_matching_operands!(self, lhs, rhs); - let mut vlist = ValueList::default(); - { - let pool = &mut self.data_flow_graph_mut().value_lists; - vlist.extend([rhs, lhs], pool); - } - self.PrimOp(Opcode::AssertEq, Type::Unit, vlist, span).0 - } - - fn assert_eq_imm(mut self, lhs: Immediate, rhs: Value, span: SourceSpan) -> Inst { - require_integer!(self, rhs); - let mut vlist = ValueList::default(); - { - let pool = &mut self.data_flow_graph_mut().value_lists; - vlist.push(rhs, pool); - } - self.PrimOpImm(Opcode::AssertEq, Type::Unit, lhs, vlist, span) - .0 - } - - signed_integer_literal!(1, bool); - integer_literal!(8); - integer_literal!(16); - integer_literal!(32); - integer_literal!(64); - - fn felt(self, i: Felt, span: SourceSpan) -> Value { - into_first_result!(self.UnaryImm(Opcode::ImmFelt, Type::Felt, Immediate::Felt(i), span)) - } - - fn f64(self, f: f64, span: SourceSpan) -> Value { - into_first_result!(self.UnaryImm(Opcode::ImmF64, Type::F64, Immediate::F64(f), span)) - } - - fn character(self, c: char, span: SourceSpan) -> Value { - self.i32((c as u32) as i32, span) - } - - fn alloca(self, ty: Type, span: SourceSpan) -> Value { - into_first_result!(self.PrimOp( - Opcode::Alloca, - Type::Ptr(Box::new(ty)), - ValueList::default(), - span, - )) - } - - fn mem_grow(mut self, value: Value, span: SourceSpan) -> Value { - require_integer!(self, value, Type::U32); - let mut vlist = ValueList::default(); - { - let pool = &mut self.data_flow_graph_mut().value_lists; - vlist.push(value, pool); - } - into_first_result!(self.PrimOp(Opcode::MemGrow, Type::I32, vlist, span,)) - } - - /// Get a [GlobalValue] which represents the address of a global variable whose symbol is `name` - /// - /// On it's own, this does nothing, you must use the resulting [GlobalValue] with a builder - /// that expects one as an argument, or use `global_value` to obtain a [Value] from it. - fn symbol>(self, name: S, span: SourceSpan) -> GlobalValue { - self.symbol_relative(name, 0, span) - } - - /// Same semantics as `symbol`, but applies a constant offset to the address of the given symbol. - /// - /// If the offset is zero, this is equivalent to `symbol` - fn symbol_relative>( - mut self, - name: S, - offset: i32, - span: SourceSpan, - ) -> GlobalValue { - self.data_flow_graph_mut() - .create_global_value(GlobalValueData::Symbol { - name: Ident::new(Symbol::intern(name.as_ref()), span), - offset, - }) - } - - /// Get the address of a global variable whose symbol is `name` - /// - /// The type of the pointer produced is given as `ty`. It is up to the caller - /// to ensure that loading memory from that pointer is valid for the provided - /// type. - fn symbol_addr>(self, name: S, ty: Type, span: SourceSpan) -> Value { - self.symbol_relative_addr(name, 0, ty, span) - } - - /// Same semantics as `symbol_addr`, but applies a constant offset to the address of the given symbol. - /// - /// If the offset is zero, this is equivalent to `symbol_addr` - fn symbol_relative_addr>( - mut self, - name: S, - offset: i32, - ty: Type, - span: SourceSpan, - ) -> Value { - assert!(ty.is_pointer(), "expected pointer type, got '{}'", &ty); - let gv = self - .data_flow_graph_mut() - .create_global_value(GlobalValueData::Symbol { - name: Ident::new(Symbol::intern(name.as_ref()), span), - offset, - }); - into_first_result!(self.Global(gv, ty, span)) - } - - /// Loads a value of type `ty` from the global variable whose symbol is `name`. - /// - /// NOTE: There is no requirement that the memory contents at the given symbol - /// contain a valid value of type `ty`. That is left entirely up the caller to - /// guarantee at a higher level. - fn load_symbol>(self, name: S, ty: Type, span: SourceSpan) -> Value { - self.load_symbol_relative(name, ty, 0, span) - } - - /// Same semantics as `load_symbol`, but a constant offset is applied to the address before issuing the load. - fn load_symbol_relative>( - mut self, - name: S, - ty: Type, - offset: i32, - span: SourceSpan, - ) -> Value { - let base = self - .data_flow_graph_mut() - .create_global_value(GlobalValueData::Symbol { - name: Ident::new(Symbol::intern(name.as_ref()), span), - offset: 0, - }); - self.load_global_relative(base, ty, offset, span) - } - - /// Loads a value of type `ty` from the address represented by `addr` - /// - /// NOTE: There is no requirement that the memory contents at the given symbol - /// contain a valid value of type `ty`. That is left entirely up the caller to - /// guarantee at a higher level. - fn load_global(self, addr: GlobalValue, ty: Type, span: SourceSpan) -> Value { - self.load_global_relative(addr, ty, 0, span) - } - - /// Same semantics as `load_global_relative`, but a constant offset is applied to the address before issuing the load. - fn load_global_relative( - mut self, - base: GlobalValue, - ty: Type, - offset: i32, - span: SourceSpan, - ) -> Value { - if let GlobalValueData::Load { - ty: ref base_ty, .. - } = self.data_flow_graph().global_value(base) - { - // If the base global is a load, the target address cannot be computed until runtime, - // so expand this to the appropriate sequence of instructions to do so in that case - assert!( - base_ty.is_pointer(), - "expected global value to have pointer type" - ); - let base_ty = base_ty.clone(); - let base = self.ins().load_global(base, base_ty.clone(), span); - let addr = self.ins().ptrtoint(base, Type::U32, span); - let offset_addr = if offset >= 0 { - self.ins() - .add_imm_checked(addr, Immediate::U32(offset as u32), span) - } else { - self.ins() - .sub_imm_checked(addr, Immediate::U32(offset.unsigned_abs()), span) - }; - let ptr = self.ins().inttoptr(offset_addr, base_ty, span); - self.load(ptr, span) - } else { - // The global address can be computed statically - let gv = self - .data_flow_graph_mut() - .create_global_value(GlobalValueData::Load { - base, - offset, - ty: ty.clone(), - }); - into_first_result!(self.Global(gv, ty, span)) - } - } - - /// Computes an address relative to the pointer produced by `base`, by applying an offset - /// given by multiplying `offset` * the size in bytes of `unit_ty`. - /// - /// The type of the pointer produced is the same as the type of the pointer given by `base` - /// - /// This is useful in some scenarios where `load_global_relative` is not, namely when computing - /// the effective address of an element of an array stored in a global variable. - fn global_addr_offset( - mut self, - base: GlobalValue, - offset: i32, - unit_ty: Type, - span: SourceSpan, - ) -> Value { - if let GlobalValueData::Load { - ty: ref base_ty, .. - } = self.data_flow_graph().global_value(base) - { - // If the base global is a load, the target address cannot be computed until runtime, - // so expand this to the appropriate sequence of instructions to do so in that case - assert!( - base_ty.is_pointer(), - "expected global value to have pointer type" - ); - let base_ty = base_ty.clone(); - let base = self.ins().load_global(base, base_ty.clone(), span); - let addr = self.ins().ptrtoint(base, Type::U32, span); - let unit_size: i32 = unit_ty - .size_in_bytes() - .try_into() - .expect("invalid type: size is larger than 2^32"); - let computed_offset = unit_size * offset; - let offset_addr = if computed_offset >= 0 { - self.ins() - .add_imm_checked(addr, Immediate::U32(offset as u32), span) - } else { - self.ins() - .sub_imm_checked(addr, Immediate::U32(offset.unsigned_abs()), span) - }; - let ptr = self.ins().inttoptr(offset_addr, base_ty, span); - self.load(ptr, span) - } else { - // The global address can be computed statically - let gv = self - .data_flow_graph_mut() - .create_global_value(GlobalValueData::IAddImm { - base, - offset, - ty: unit_ty.clone(), - }); - let ty = self.data_flow_graph().global_type(gv); - into_first_result!(self.Global(gv, ty, span)) - } - } - - /// Loads a value of the type pointed to by the given pointer, on to the stack - /// - /// NOTE: This function will panic if `ptr` is not a pointer typed value - fn load(self, addr: Value, span: SourceSpan) -> Value { - let ty = require_pointee!(self, addr).clone(); - let data = Instruction::Load(LoadOp { - op: Opcode::Load, - addr, - ty: ty.clone(), - }); - into_first_result!(self.build(data, Type::Ptr(Box::new(ty)), span)) - } - - /// Stores `value` to the address given by `ptr` - /// - /// NOTE: This function will panic if the pointer and pointee types do not match - fn store(mut self, ptr: Value, value: Value, span: SourceSpan) -> Inst { - let pointee_ty = require_pointee!(self, ptr); - let value_ty = self.data_flow_graph().value_type(value); - assert_eq!( - pointee_ty, value_ty, - "expected value to be a {}, got {}", - pointee_ty, value_ty - ); - let mut vlist = ValueList::default(); - { - let dfg = self.data_flow_graph_mut(); - vlist.extend([ptr, value], &mut dfg.value_lists); - } - self.PrimOp(Opcode::Store, Type::Unit, vlist, span).0 - } - - /// Copies `count` values from the memory at address `src`, to the memory at address `dst`. - /// - /// The unit size for `count` is determined by the `src` pointer type, i.e. a pointer to u8 - /// will copy one `count` bytes, a pointer to u16 will copy `count * 2` bytes, and so on. - /// - /// NOTE: The source and destination pointer types must match, or this function will panic. - fn memcpy(mut self, src: Value, dst: Value, count: Value, span: SourceSpan) -> Inst { - require_integer!(self, count); - let src_ty = require_pointer!(self, src); - let dst_ty = require_pointer!(self, dst); - assert_eq!( - src_ty, dst_ty, - "the source and destination pointers must be the same type" - ); - let mut vlist = ValueList::default(); - { - let dfg = self.data_flow_graph_mut(); - vlist.extend([src, dst, count], &mut dfg.value_lists); - } - self.PrimOp(Opcode::MemCpy, Type::Unit, vlist, span).0 - } - - /// This is a cast operation that permits performing arithmetic on pointer values - /// by casting a pointer to a specified integral type. - fn ptrtoint(self, arg: Value, ty: Type, span: SourceSpan) -> Value { - require_pointer!(self, arg); - assert!(ty.is_integer(), "expected integral type, got {}", &ty); - into_first_result!(self.Unary(Opcode::PtrToInt, ty, arg, span)) - } - - /// This is the inverse of `ptrtoint`, used to recover a pointer that was - /// previously cast to an integer type. It may also be used to cast arbitrary - /// integer values to pointers. - /// - /// In both cases, use of the resulting pointer must not violate the semantics - /// of the higher level language being represented in Miden IR. - fn inttoptr(self, arg: Value, ty: Type, span: SourceSpan) -> Value { - require_unsigned_integer!(self, arg); - assert!(ty.is_pointer(), "expected pointer type, got {}", &ty); - into_first_result!(self.Unary(Opcode::IntToPtr, ty, arg, span)) - } - - /// This is an intrinsic which derives a new pointer from an existing pointer to an aggregate. - /// - /// In short, this represents the common need to calculate a new pointer from an existing pointer, - /// but without losing provenance of the original pointer. It is specifically intended for use - /// in obtaining a pointer to an element/field of an array/struct, of the correct type, given a - /// well typed pointer to the aggregate. - /// - /// This function will panic if the pointer is not to an aggregate type - /// - /// The new pointer is derived by statically navigating the structure of the pointee type, using - /// `offsets` to guide the traversal. Initially, the first offset is relative to the original - /// pointer, where `0` refers to the base/first field of the object. The second offset is then - /// relative to the base of the object selected by the first offset, and so on. Offsets must remain - /// in bounds, any attempt to index outside a type's boundaries will result in a panic. - fn getelementptr(mut self, ptr: Value, mut indices: &[usize], span: SourceSpan) -> Value { - let mut ty = require_pointee!(self, ptr); - assert!( - !indices.is_empty(), - "getelementptr requires at least one index" - ); - - // Calculate the offset in bytes from `ptr` to get the element pointer - let mut offset = 0; - while let Some((index, rest)) = indices.split_first().map(|(i, rs)| (*i, rs)) { - indices = rest; - match ty { - Type::Array(ref element_ty, len) => { - assert!( - index < *len, - "invalid getelementptr: index of {} is out of bounds for {}", - index, - ty - ); - let element_size = element_ty.size_in_bytes(); - let min_align = element_ty.min_alignment(); - let padded_element_size = element_size.align_up(min_align); - ty = element_ty; - offset += padded_element_size * index; - } - Type::Struct(ref struct_ty) => { - assert!( - index < struct_ty.len(), - "invalid getelementptr: index of {} is out of bounds for {}", - index, - ty - ); - let field = struct_ty.get(index); - offset += field.offset as usize; - ty = &field.ty; - } - other => panic!( - "invalid getelementptr: cannot index values of type {}", - other - ), - } - } - - // Emit the instruction sequence for offsetting the pointer - let ty = Type::Ptr(Box::new(ty.clone())); - // Cast the pointer to an integer - let addr = self.ins().ptrtoint(ptr, Type::U32, span); - // Add the element offset to the pointer - let offset: u32 = offset.try_into().expect( - "invalid getelementptr type: computed offset cannot possibly fit in linear memory", - ); - let new_addr = self - .ins() - .add_imm_checked(addr, Immediate::U32(offset), span); - // Cast back to a pointer to the selected element type - self.inttoptr(new_addr, ty, span) - } - - /// Cast `arg` to a value of type `ty` - /// - /// NOTE: This is only valid for numeric to numeric, or pointer to pointer casts. - /// For numeric to pointer, or pointer to numeric casts, use `inttoptr` and `ptrtoint` - /// respectively. - fn cast(self, arg: Value, ty: Type, span: SourceSpan) -> Value { - let arg_ty = self.data_flow_graph().value_type(arg); - let both_numeric = arg_ty.is_numeric() && ty.is_numeric(); - let both_pointers = arg_ty.is_pointer() && ty.is_pointer(); - let kind_matches = both_numeric || both_pointers; - assert!( - kind_matches, - "invalid cast, expected source and target types to be of the same kind, where a kind is either numeric or pointer: value is of type {}, and target type is {}", - &arg_ty, &ty - ); - into_first_result!(self.Unary(Opcode::Cast, ty, arg, span)) - } - - /// Truncates an integral value as necessary to fit in `ty`. - /// - /// NOTE: Truncating a value into a larger type has undefined behavior, it is - /// equivalent to extending a value without doing anything with the new high-order - /// bits of the resulting value. - fn trunc(self, arg: Value, ty: Type, span: SourceSpan) -> Value { - require_integer!(self, arg); - assert!(ty.is_integer(), "expected integer type, got {}", &ty); - into_first_result!(self.Unary(Opcode::Trunc, ty, arg, span)) - } - - /// Extends an integer into a larger integeral type, by zero-extending the value, - /// i.e. the new high-order bits of the resulting value will be all zero. - /// - /// NOTE: This function will panic if `ty` is smaller than `arg`. - /// - /// If `arg` is the same type as `ty`, `arg` is returned as-is - fn zext(self, arg: Value, ty: Type, span: SourceSpan) -> Value { - let arg_ty = require_integer!(self, arg); - assert!(ty.is_integer(), "expected integer type, got {}", &ty); - if arg_ty == &ty { - return arg; - } - assert!( - arg_ty.size_in_bits() <= ty.size_in_bits(), - "invalid extension: target type ({:?}) is smaller than the argument type ({:?})", - &ty, - &arg_ty - ); - into_first_result!(self.Unary(Opcode::Zext, ty, arg, span)) - } - - /// Extends an integer into a larger integeral type, by sign-extending the value, - /// i.e. the new high-order bits of the resulting value will all match the sign bit. - /// - /// NOTE: This function will panic if `ty` is smaller than `arg`. - /// - /// If `arg` is the same type as `ty`, `arg` is returned as-is - fn sext(self, arg: Value, ty: Type, span: SourceSpan) -> Value { - let arg_ty = require_integer!(self, arg); - assert!(ty.is_integer(), "expected integer type, got {}", &ty); - if arg_ty == &ty { - return arg; - } - assert!( - arg_ty.size_in_bits() <= ty.size_in_bits(), - "invalid extension: target type ({:?}) is smaller than the argument type ({:?})", - &ty, - &arg_ty - ); - into_first_result!(self.Unary(Opcode::Sext, ty, arg, span)) - } - - binary_int_op_with_overflow!(add, Opcode::Add); - binary_int_op_with_overflow!(sub, Opcode::Sub); - binary_int_op_with_overflow!(mul, Opcode::Mul); - checked_binary_int_op!(div, Opcode::Div); - binary_int_op!(min, Opcode::Min); - binary_int_op!(max, Opcode::Max); - checked_binary_int_op!(r#mod, Opcode::Mod); - checked_binary_int_op!(divmod, Opcode::DivMod); - binary_int_op!(exp, Opcode::Exp); - binary_boolean_op!(and, Opcode::And); - binary_int_op!(band, Opcode::Band); - binary_boolean_op!(or, Opcode::Or); - binary_int_op!(bor, Opcode::Bor); - binary_boolean_op!(xor, Opcode::Xor); - binary_int_op!(bxor, Opcode::Bxor); - binary_int_op_with_overflow!(shl, Opcode::Shl); - binary_int_op_with_overflow!(shr, Opcode::Shr); - binary_int_op!(rotl, Opcode::Rotl); - binary_int_op!(rotr, Opcode::Rotr); - unary_int_op!(neg, Opcode::Neg); - unary_int_op!(inv, Opcode::Inv); - unary_int_op_with_overflow!(incr, Opcode::Incr); - unary_int_op!(pow2, Opcode::Pow2); - unary_boolean_op!(not, Opcode::Not); - unary_int_op!(bnot, Opcode::Bnot); - unary_int_op!(popcnt, Opcode::Popcnt); - - fn eq(self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - into_first_result!(self.Binary(Opcode::Eq, Type::I1, lhs, rhs, span)) - } - - fn eq_imm(self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - assert_eq!( - lty, - imm.ty(), - "expected immediate to be the same type as non-immediate operand", - ); - into_first_result!(self.BinaryImm(Opcode::Eq, Type::I1, lhs, imm, span)) - } - - fn neq(self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - into_first_result!(self.Binary(Opcode::Neq, Type::I1, lhs, rhs, span)) - } - - fn neq_imm(self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - assert_eq!( - lty, - imm.ty(), - "expected immediate to be the same type as non-immediate operand", - ); - into_first_result!(self.BinaryImm(Opcode::Neq, Type::I1, lhs, imm, span)) - } - - fn gt(self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - require_integer!(self, lhs); - require_integer!(self, rhs); - into_first_result!(self.Binary(Opcode::Gt, Type::I1, lhs, rhs, span)) - } - - fn gt_imm(self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - assert_eq!( - lty, - imm.ty(), - "expected immediate to be the same type as non-immediate operand", - ); - into_first_result!(self.BinaryImm(Opcode::Gt, Type::I1, lhs, imm, span)) - } - - fn gte(self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - require_integer!(self, lhs); - require_integer!(self, rhs); - into_first_result!(self.Binary(Opcode::Gte, Type::I1, lhs, rhs, span)) - } - - fn gte_imm(self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - assert_eq!( - lty, - imm.ty(), - "expected immediate to be the same type as non-immediate operand", - ); - into_first_result!(self.BinaryImm(Opcode::Gte, Type::I1, lhs, imm, span)) - } - - fn lt(self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - require_integer!(self, lhs); - require_integer!(self, rhs); - into_first_result!(self.Binary(Opcode::Lt, Type::I1, lhs, rhs, span)) - } - - fn lt_imm(self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - assert_eq!( - lty, - imm.ty(), - "expected immediate to be the same type as non-immediate operand", - ); - into_first_result!(self.BinaryImm(Opcode::Lt, Type::I1, lhs, imm, span)) - } - - fn lte(self, lhs: Value, rhs: Value, span: SourceSpan) -> Value { - require_integer!(self, lhs); - require_integer!(self, rhs); - into_first_result!(self.Binary(Opcode::Lte, Type::I1, lhs, rhs, span)) - } - - fn lte_imm(self, lhs: Value, imm: Immediate, span: SourceSpan) -> Value { - let lty = assert_integer_operands!(self, lhs); - assert_eq!( - lty, - imm.ty(), - "expected immediate to be the same type as non-immediate operand", - ); - into_first_result!(self.BinaryImm(Opcode::Lte, Type::I1, lhs, imm, span)) - } - - #[allow(clippy::wrong_self_convention)] - fn is_odd(self, value: Value, span: SourceSpan) -> Value { - require_integer!(self, value); - into_first_result!(self.Unary(Opcode::IsOdd, Type::I1, value, span)) - } - - fn call(mut self, callee: FunctionIdent, args: &[Value], span: SourceSpan) -> Inst { - let mut vlist = ValueList::default(); - { - let dfg = self.data_flow_graph_mut(); - assert!( - dfg.get_import(&callee).is_some(), - "must import callee ({}) before calling it", - &callee - ); - vlist.extend(args.iter().copied(), &mut dfg.value_lists); - } - self.Call(Opcode::Call, callee, vlist, span).0 - } - - fn syscall(mut self, callee: FunctionIdent, args: &[Value], span: SourceSpan) -> Inst { - let mut vlist = ValueList::default(); - { - let dfg = self.data_flow_graph_mut(); - assert!( - dfg.get_import(&callee).is_some(), - "must import callee ({}) before calling it", - &callee - ); - vlist.extend(args.iter().copied(), &mut dfg.value_lists); - } - self.Call(Opcode::Syscall, callee, vlist, span).0 - } - - fn select(mut self, cond: Value, a: Value, b: Value, span: SourceSpan) -> Value { - let mut vlist = ValueList::default(); - let ty = require_matching_operands!(self, a, b).clone(); - { - let dfg = self.data_flow_graph_mut(); - assert_eq!( - dfg.value_type(cond), - &Type::I1, - "select expect the type of the condition to be i1" - ); - vlist.extend([cond, a, b], &mut dfg.value_lists); - } - into_first_result!(self.PrimOp(Opcode::Select, ty, vlist, span)) - } - - fn br(mut self, block: Block, args: &[Value], span: SourceSpan) -> Inst { - let mut vlist = ValueList::default(); - { - let pool = &mut self.data_flow_graph_mut().value_lists; - vlist.extend(args.iter().copied(), pool); - } - self.Br(Opcode::Br, Type::Unit, block, vlist, span).0 - } - - fn cond_br( - mut self, - cond: Value, - then_dest: Block, - then_args: &[Value], - else_dest: Block, - else_args: &[Value], - span: SourceSpan, - ) -> Inst { - require_integer!(self, cond, Type::I1); - let mut then_vlist = ValueList::default(); - let mut else_vlist = ValueList::default(); - { - let pool = &mut self.data_flow_graph_mut().value_lists; - then_vlist.extend(then_args.iter().copied(), pool); - else_vlist.extend(else_args.iter().copied(), pool); - } - 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 { - require_integer!(self, arg, Type::U32); - self.Switch(arg, arms, default, span).0 - } - - fn ret(mut self, returning: Option, span: SourceSpan) -> Inst { - let mut vlist = ValueList::default(); - if let Some(value) = returning { - let pool = &mut self.data_flow_graph_mut().value_lists; - vlist.push(value, pool); - } - self.Ret(vlist, span).0 - } - - fn ret_imm(self, arg: Immediate, span: SourceSpan) -> Inst { - let data = Instruction::RetImm(RetImm { - op: Opcode::Ret, - arg, - }); - self.build(data, Type::Unit, span).0 - } - - fn unreachable(self, span: SourceSpan) -> Inst { - let data = Instruction::PrimOp(PrimOp { - op: Opcode::Unreachable, - args: ValueList::default(), - }); - self.build(data, Type::Never, span).0 - } - - fn inline_asm( - self, - args: &[Value], - results: impl IntoIterator, - span: SourceSpan, - ) -> MasmBuilder { - MasmBuilder::new(self, args, results.into_iter().collect(), span) - } - - #[allow(non_snake_case)] - fn CondBr( - self, - cond: Value, - then_dest: Block, - then_args: ValueList, - else_dest: Block, - else_args: ValueList, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::CondBr(CondBr { - op: Opcode::CondBr, - cond, - then_dest: (then_dest, then_args), - else_dest: (else_dest, else_args), - }); - self.build(data, Type::Unit, span) - } - - #[allow(non_snake_case)] - fn Br( - self, - op: Opcode, - ty: Type, - destination: Block, - args: ValueList, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::Br(Br { - op, - destination, - args, - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn Switch( - self, - arg: Value, - arms: Vec<(u32, Block)>, - default: Block, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::Switch(Switch { - op: Opcode::Switch, - arg, - arms, - default, - }); - self.build(data, Type::Unit, span) - } - - #[allow(non_snake_case)] - fn Ret(self, args: ValueList, span: SourceSpan) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::Ret(Ret { - op: Opcode::Ret, - args, - }); - self.build(data, Type::Unit, span) - } - - #[allow(non_snake_case)] - fn Call( - self, - op: Opcode, - callee: FunctionIdent, - args: ValueList, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::Call(Call { op, callee, args }); - self.build(data, Type::Unit, span) - } - - #[allow(non_snake_case)] - fn Binary( - self, - op: Opcode, - ty: Type, - lhs: Value, - rhs: Value, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::BinaryOp(BinaryOp { - op, - overflow: None, - // We place arguments in stack order for more efficient codegen - args: [rhs, lhs], - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn BinaryWithOverflow( - self, - op: Opcode, - ty: Type, - lhs: Value, - rhs: Value, - overflow: Overflow, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::BinaryOp(BinaryOp { - op, - overflow: Some(overflow), - // We place arguments in stack order for more efficient codegen - args: [rhs, lhs], - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn BinaryImm( - self, - op: Opcode, - ty: Type, - arg: Value, - imm: Immediate, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::BinaryOpImm(BinaryOpImm { - op, - overflow: None, - arg, - imm, - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn BinaryImmWithOverflow( - self, - op: Opcode, - ty: Type, - arg: Value, - imm: Immediate, - overflow: Overflow, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::BinaryOpImm(BinaryOpImm { - op, - overflow: Some(overflow), - arg, - imm, - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn Unary( - self, - op: Opcode, - ty: Type, - arg: Value, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::UnaryOp(UnaryOp { - op, - overflow: None, - arg, - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn UnaryWithOverflow( - self, - op: Opcode, - ty: Type, - arg: Value, - overflow: Overflow, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::UnaryOp(UnaryOp { - op, - overflow: Some(overflow), - arg, - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn UnaryImm( - self, - op: Opcode, - ty: Type, - imm: Immediate, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::UnaryOpImm(UnaryOpImm { - op, - overflow: None, - imm, - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn UnaryImmWithOverflow( - self, - op: Opcode, - ty: Type, - imm: Immediate, - overflow: Overflow, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::UnaryOpImm(UnaryOpImm { - op, - overflow: Some(overflow), - imm, - }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn Test( - self, - op: Opcode, - ret: Type, - arg: Value, - ty: Type, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::Test(Test { op, arg, ty }); - self.build(data, ret, span) - } - - #[allow(non_snake_case)] - fn PrimOp( - self, - op: Opcode, - ty: Type, - args: ValueList, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::PrimOp(PrimOp { op, args }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn PrimOpImm( - self, - op: Opcode, - ty: Type, - imm: Immediate, - args: ValueList, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::PrimOpImm(PrimOpImm { op, imm, args }); - self.build(data, ty, span) - } - - #[allow(non_snake_case)] - fn Global( - self, - global: GlobalValue, - ty: Type, - span: SourceSpan, - ) -> (Inst, &'f mut DataFlowGraph) { - let data = Instruction::GlobalValue(GlobalValueOp { - op: Opcode::GlobalValue, - global, - }); - self.build(data, ty, span) - } -} - -impl<'f, T: InstBuilderBase<'f>> InstBuilder<'f> for T {} diff --git a/hir/src/constants.rs b/hir/src/constants.rs deleted file mode 100644 index cb599b72a..000000000 --- a/hir/src/constants.rs +++ /dev/null @@ -1,181 +0,0 @@ -use std::{collections::BTreeMap, fmt, str::FromStr}; - -use cranelift_entity::{entity_impl, EntityRef}; - -pub trait IntoBytes { - fn into_bytes(self) -> Vec; -} -impl IntoBytes for Vec { - #[inline(always)] - fn into_bytes(self) -> Vec { - self - } -} -impl IntoBytes for i8 { - #[inline] - fn into_bytes(self) -> Vec { - vec![self as u8] - } -} -impl IntoBytes for i16 { - #[inline] - fn into_bytes(self) -> Vec { - self.to_le_bytes().to_vec() - } -} - -/// A handle to a constant -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Constant(u32); -entity_impl!(Constant, "const"); - -/// This type represents the raw data of a constant. -/// -/// The data is expected to be in little-endian order. -#[derive(Debug, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] -pub struct ConstantData(Vec); -impl ConstantData { - /// Return the number of bytes in the constant. - pub fn len(&self) -> usize { - self.0.len() - } - - /// Check if the constant contains any bytes. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Return the data as a slice. - pub fn as_slice(&self) -> &[u8] { - self.0.as_slice() - } - - /// Append bytes to this constant - pub fn append(mut self, bytes: impl IntoBytes) -> Self { - let mut bytes = bytes.into_bytes(); - self.0.append(&mut bytes); - self - } - - /// Grow the size of the constant data in bytes to `expected_size`, zero-extending - /// the data by writing zeroes to the newly-added high-order bytes. - pub fn zext(mut self, expected_size: usize) -> Self { - assert!( - self.len() <= expected_size, - "the constant is already larger than {} bytes", - expected_size - ); - self.0.resize(expected_size, 0); - self - } -} -impl FromIterator for ConstantData { - fn from_iter>(iter: T) -> Self { - Self(iter.into_iter().collect()) - } -} -impl From> for ConstantData { - fn from(v: Vec) -> Self { - Self(v) - } -} -impl From<[u8; N]> for ConstantData { - fn from(v: [u8; N]) -> Self { - Self(v.to_vec()) - } -} -impl From<&[u8]> for ConstantData { - fn from(v: &[u8]) -> Self { - Self(v.to_vec()) - } -} -impl fmt::Display for ConstantData { - /// Print the constant data in hexadecimal format, e.g. 0x000102030405060708090a0b0c0d0e0f. - /// - /// The printed form of the constant renders the bytes in big-endian order, for readability. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if !self.is_empty() { - write!(f, "0x")?; - for b in self.0.iter().rev() { - write!(f, "{:02x}", b)?; - } - } - Ok(()) - } -} -impl FromStr for ConstantData { - type Err = (); - - fn from_str(s: &str) -> Result { - let len = s.len(); - if len % 2 != 0 { - return Err(()); - } - let pairs = len / 2; - let mut data = Vec::with_capacity(pairs); - let mut chars = s.chars(); - while let Some(a) = chars.next() { - let a = a.to_digit(16).ok_or(())?; - let b = chars.next().unwrap().to_digit(16).ok_or(())?; - data.push(((a << 4) + b) as u8); - } - Ok(Self(data)) - } -} - -/// This maintains the storage for constants used within a function -#[derive(Default)] -pub struct ConstantPool { - /// This mapping maintains the insertion order as long as Constants are created with - /// sequentially increasing integers. - /// - /// It is important that, by construction, no entry in that list gets removed. If that ever - /// need to happen, don't forget to update the `Constant` generation scheme. - constants: BTreeMap, - - /// Mapping of hashed `ConstantData` to the index into the other hashmap. - /// - /// This allows for deduplication of entries into the `handles_to_values` mapping. - cache: BTreeMap, -} -impl ConstantPool { - /// Returns true if the pool is empty - pub fn is_empty(&self) -> bool { - self.constants.is_empty() - } - - /// Returns the number of constants in this pool - pub fn len(&self) -> usize { - self.constants.len() - } - - /// Retrieve the constant data given a handle. - pub fn get(&self, id: Constant) -> &ConstantData { - &self.constants[&id] - } - - /// Returns true if this pool contains the given constant data - pub fn contains(&self, data: &ConstantData) -> bool { - self.cache.contains_key(data) - } - - /// Insert constant data into the pool, returning a handle for later referencing; when constant - /// data is inserted that is a duplicate of previous constant data, the existing handle will be - /// returned. - pub fn insert(&mut self, data: ConstantData) -> Constant { - if let Some(cst) = self.cache.get(&data) { - return *cst; - } - - let id = Constant::new(self.len()); - self.constants.insert(id, data.clone()); - self.cache.insert(data, id); - id - } - - /// Traverse the contents of the pool - #[inline] - pub fn iter(&self) -> impl Iterator { - self.constants.iter().map(|(k, v)| (*k, v)) - } -} diff --git a/hir/src/dataflow.rs b/hir/src/dataflow.rs deleted file mode 100644 index 1050d895b..000000000 --- a/hir/src/dataflow.rs +++ /dev/null @@ -1,763 +0,0 @@ -use std::ops::{Deref, Index, IndexMut}; - -use cranelift_entity::{PrimaryMap, SecondaryMap}; -use intrusive_collections::UnsafeRef; -use rustc_hash::FxHashMap; -use smallvec::SmallVec; - -use miden_diagnostics::{SourceSpan, Span, Spanned}; - -use super::*; - -pub struct DataFlowGraph { - pub entry: Block, - pub attrs: AttributeSet, - pub blocks: OrderedArenaMap, - pub insts: ArenaMap, - pub results: SecondaryMap, - pub values: PrimaryMap, - pub value_lists: ValueListPool, - pub imports: FxHashMap, - pub globals: PrimaryMap, - pub constants: ConstantPool, -} -impl Default for DataFlowGraph { - fn default() -> Self { - let mut dfg = Self::new_uninit(); - let entry = dfg.blocks.create(); - dfg.entry = entry; - dfg.blocks.append(entry, BlockData::new(entry)); - dfg - } -} -impl DataFlowGraph { - /// Create a new, completely uninitialized DataFlowGraph - pub fn new_uninit() -> Self { - Self { - entry: Block::from_u32(0), - attrs: AttributeSet::default(), - blocks: OrderedArenaMap::new(), - insts: ArenaMap::new(), - results: SecondaryMap::new(), - values: PrimaryMap::new(), - value_lists: ValueListPool::new(), - imports: Default::default(), - globals: PrimaryMap::new(), - constants: ConstantPool::default(), - } - } - - /// Return the value associated with attribute `name` for this function - pub fn get_attribute(&self, name: &Q) -> Option<&AttributeValue> - where - Symbol: std::borrow::Borrow, - Q: Ord + ?Sized, - { - self.attrs.get(name) - } - - /// Return true if this function has an attributed named `name` - pub fn has_attribute(&self, name: &Q) -> bool - where - Symbol: std::borrow::Borrow, - Q: Ord + ?Sized, - { - self.attrs.has(name) - } - - /// Set the attribute `name` with `value` for this function. - pub fn set_attribute(&mut self, name: impl Into, value: impl Into) { - self.attrs.insert(name, value); - } - - /// Remove any attribute with the given name from this function - pub fn remove_attribute(&mut self, name: &Q) - where - Symbol: std::borrow::Borrow, - Q: Ord + ?Sized, - { - self.attrs.remove(name); - } - - /// Returns an [ExternalFunction] given its [FunctionIdent] - pub fn get_import(&self, id: &FunctionIdent) -> Option<&ExternalFunction> { - self.imports.get(id) - } - - /// Look up an [ExternalFunction] given it's module and function name - pub fn get_import_by_name, F: AsRef>( - &self, - module: M, - name: F, - ) -> Option<&ExternalFunction> { - let id = FunctionIdent { - module: Ident::with_empty_span(Symbol::intern(module.as_ref())), - function: Ident::with_empty_span(Symbol::intern(name.as_ref())), - }; - self.imports.get(&id) - } - - /// Returns an iterator over the [ExternalFunction]s imported by this function - pub fn imports<'a, 'b: 'a>(&'b self) -> impl Iterator + 'a { - self.imports.values() - } - - /// Imports function `name` from `module`, with `signature`, returning a [FunctionIdent] - /// corresponding to the import. - /// - /// If the function is already imported, and the signature doesn't match, `Err` is returned. - pub fn import_function( - &mut self, - module: Ident, - name: Ident, - signature: Signature, - ) -> Result { - use std::collections::hash_map::Entry; - - let id = FunctionIdent { - module, - function: name, - }; - match self.imports.entry(id) { - Entry::Vacant(entry) => { - entry.insert(ExternalFunction { id, signature }); - Ok(id) - } - Entry::Occupied(entry) => { - if entry.get().signature != signature { - Err(SymbolConflictError(id)) - } else { - Ok(id) - } - } - } - } - - /// Create a new global value reference - pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue { - self.globals.push(data) - } - - /// Gets the data associated with the given [GlobalValue] - pub fn global_value(&self, gv: GlobalValue) -> &GlobalValueData { - &self.globals[gv] - } - - /// Returns true if the given [GlobalValue] represents an address - pub fn is_global_addr(&self, gv: GlobalValue) -> bool { - match &self.globals[gv] { - GlobalValueData::Symbol { .. } | GlobalValueData::IAddImm { .. } => true, - GlobalValueData::Load { base, .. } => self.is_global_addr(*base), - } - } - - /// Returns the type of the given global value - pub fn global_type(&self, gv: GlobalValue) -> Type { - match &self.globals[gv] { - GlobalValueData::Symbol { .. } => Type::Ptr(Box::new(Type::I8)), - GlobalValueData::IAddImm { base, .. } => self.global_type(*base), - GlobalValueData::Load { ref ty, .. } => ty.clone(), - } - } - - pub fn make_value(&mut self, data: ValueData) -> Value { - self.values.push(data) - } - - pub fn value_type(&self, v: Value) -> &Type { - self.values[v].ty() - } - - pub fn value_span(&self, v: Value) -> SourceSpan { - match &self.values[v] { - ValueData::Param { span, .. } => *span, - ValueData::Inst { inst, .. } => self.inst_span(*inst), - } - } - - #[inline(always)] - pub fn value_data(&self, v: Value) -> &ValueData { - &self.values[v] - } - - pub fn set_value_type(&mut self, v: Value, ty: Type) { - self.values[v].set_type(ty) - } - - pub fn get_value(&self, v: Value) -> ValueData { - self.values[v].clone() - } - - /// Get a reference to the metadata for an instruction - #[inline(always)] - pub fn inst_node(&self, inst: Inst) -> &InstNode { - &self.insts[inst] - } - - /// Get a reference to the data for an instruction - #[inline(always)] - pub fn inst(&self, inst: Inst) -> &Instruction { - &self.insts[inst].data - } - - /// Get a mutable reference to the metadata for an instruction - #[inline(always)] - pub fn inst_mut(&mut self, inst: Inst) -> &mut Instruction { - &mut self.insts[inst].data - } - - pub fn inst_span(&self, inst: Inst) -> SourceSpan { - self.inst_node(inst).span() - } - - pub fn inst_args(&self, inst: Inst) -> &[Value] { - self.insts[inst].arguments(&self.value_lists) - } - - pub fn inst_block(&self, inst: Inst) -> Option { - let inst_data = &self.insts[inst]; - if inst_data.link.is_linked() { - Some(inst_data.block) - } else { - None - } - } - - pub fn inst_results(&self, inst: Inst) -> &[Value] { - self.results[inst].as_slice(&self.value_lists) - } - - /// Append a new instruction to the end of `block`, using the provided instruction - /// data, controlling type variable, and source span - #[inline] - pub fn append_inst( - &mut self, - block: Block, - data: Instruction, - ctrl_ty: Type, - span: SourceSpan, - ) -> Inst { - self.insert_inst( - InsertionPoint::after(ProgramPoint::Block(block)), - data, - ctrl_ty, - span, - ) - } - - /// Insert a new instruction at `ip`, using the provided instruction - /// data, controlling type variable, and source span - pub fn insert_inst( - &mut self, - ip: InsertionPoint, - data: Instruction, - ctrl_ty: Type, - span: SourceSpan, - ) -> Inst { - // Allocate the key for this instruction - let id = self.insts.alloc_key(); - let block_id = match ip.at { - ProgramPoint::Block(block) => block, - ProgramPoint::Inst(inst) => self - .inst_block(inst) - .expect("cannot insert after detached instruction"), - }; - // Store the instruction metadata - self.insts - .append(id, InstNode::new(id, block_id, Span::new(span, data))); - // Manufacture values for all of the instruction results - self.make_results(id, ctrl_ty); - // Insert the instruction based on the insertion point provided - let data = unsafe { UnsafeRef::from_raw(&self.insts[id]) }; - let block = &mut self.blocks[block_id]; - match ip { - InsertionPoint { - at: ProgramPoint::Block(_), - action: Insert::After, - } => { - // Insert at the end of this block - block.append(data); - } - InsertionPoint { - at: ProgramPoint::Block(_), - action: Insert::Before, - } => { - // Insert at the start of this block - block.prepend(data); - } - InsertionPoint { - at: ProgramPoint::Inst(inst), - action, - } => { - let mut cursor = block.cursor_mut(); - while let Some(ix) = cursor.get() { - if ix.key == inst { - break; - } - cursor.move_next(); - } - assert!(!cursor.is_null()); - match action { - // Insert just after `inst` in this block - Insert::After => cursor.insert_after(data), - // Insert just before `inst` in this block - Insert::Before => cursor.insert_before(data), - } - } - } - id - } - - /// Create a new instruction which is a clone of `inst`, but detached from any block. - /// - /// NOTE: The instruction is in a temporarily invalid state, because if it has arguments, - /// they will reference values from the scope of the original instruction, but the clone - /// hasn't been inserted anywhere yet. It is up to the caller to ensure that the cloned - /// instruction is updated appropriately once inserted. - pub fn clone_inst(&mut self, inst: Inst) -> Inst { - let id = self.insts.alloc_key(); - let span = self.insts[inst].data.span(); - let data = self.insts[inst].data.deep_clone(&mut self.value_lists); - self.insts.append( - id, - InstNode::new(id, Block::default(), Span::new(span, data)), - ); - - // Derive results for the cloned instruction using the results - // of the original instruction - let results = SmallVec::<[Value; 1]>::from_slice(self.inst_results(inst)); - for result in results.into_iter() { - let ty = self.value_type(result).clone(); - self.append_result(id, ty); - } - id - } - - /// Create a `ReplaceBuilder` that will replace `inst` with a new instruction in-place. - pub fn replace(&mut self, inst: Inst) -> ReplaceBuilder { - ReplaceBuilder::new(self, inst) - } - - pub fn append_result(&mut self, inst: Inst, ty: Type) -> Value { - let res = self.values.next_key(); - let num = self.results[inst].push(res, &mut self.value_lists); - debug_assert!(num <= u16::MAX as usize, "too many result values"); - self.make_value(ValueData::Inst { - ty, - inst, - num: num as u16, - }) - } - - pub fn first_result(&self, inst: Inst) -> Value { - self.results[inst] - .first(&self.value_lists) - .expect("instruction has no results") - } - - pub fn has_results(&self, inst: Inst) -> bool { - !self.results[inst].is_empty() - } - - fn make_results(&mut self, inst: Inst, ctrl_ty: Type) { - self.results[inst].clear(&mut self.value_lists); - - let opcode = self.insts[inst].opcode(); - if let Some(fdata) = self.call_signature(inst) { - let results = - SmallVec::<[Type; 2]>::from_iter(fdata.results().iter().map(|abi| abi.ty.clone())); - for ty in results.into_iter() { - self.append_result(inst, ty); - } - } else { - match self.insts[inst].data.deref() { - Instruction::InlineAsm(ref asm) => { - let results = asm.results.clone(); - for ty in results.into_iter() { - self.append_result(inst, ty); - } - } - ix => { - let overflow = ix.overflow(); - for ty in opcode.results(overflow, ctrl_ty).into_iter() { - self.append_result(inst, ty); - } - } - } - } - } - - pub(super) fn replace_results(&mut self, inst: Inst, ctrl_ty: Type) { - let opcode = self.insts[inst].opcode(); - let old_results = - SmallVec::<[Value; 1]>::from_slice(self.results[inst].as_slice(&self.value_lists)); - let mut new_results = SmallVec::<[Type; 1]>::default(); - if let Some(fdata) = self.call_signature(inst) { - new_results.extend(fdata.results().iter().map(|p| p.ty.clone())); - } else { - match self.insts[inst].data.deref() { - Instruction::InlineAsm(ref asm) => { - new_results.extend(asm.results.as_slice().iter().cloned()); - } - ix => { - let overflow = ix.overflow(); - new_results = opcode.results(overflow, ctrl_ty); - } - } - } - let old_results_len = old_results.len(); - let new_results_len = new_results.len(); - if old_results_len > new_results_len { - self.results[inst].truncate(new_results_len, &mut self.value_lists); - } - for (index, ty) in new_results.into_iter().enumerate() { - if index >= old_results_len { - // We must allocate a new value for this result - self.append_result(inst, ty); - } else { - // We're updating the old value with a new type - let value = old_results[index]; - self.values[value].set_type(ty); - } - } - } - - /// 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); - for arg in args.iter_mut() { - if arg == &value { - *arg = replacement; - } - } - } - Instruction::CondBr(CondBr { - ref mut cond, - then_dest: (_, ref mut then_args), - else_dest: (_, ref mut else_args), - .. - }) => { - if cond == &value { - *cond = replacement; - } - let then_args = then_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); - for arg in else_args.iter_mut() { - if arg == &value { - *arg = replacement; - } - } - } - ix => { - for arg in ix.arguments_mut(&mut self.value_lists) { - if arg == &value { - *arg = replacement; - } - } - } - } - } - - pub fn pp_block(&self, pp: ProgramPoint) -> Block { - match pp { - ProgramPoint::Block(block) => block, - ProgramPoint::Inst(inst) => self.inst_block(inst).expect("program point not in layout"), - } - } - - pub fn pp_cmp(&self, a: A, b: B) -> core::cmp::Ordering - where - A: Into, - B: Into, - { - let a = a.into(); - let b = b.into(); - debug_assert_eq!(self.pp_block(a), self.pp_block(b)); - let a_seq = match a { - ProgramPoint::Block(_) => 0, - ProgramPoint::Inst(inst) => { - let block = self.insts[inst].block; - self.blocks[block].insts().position(|i| i == inst).unwrap() + 1 - } - }; - let b_seq = match b { - ProgramPoint::Block(_) => 0, - ProgramPoint::Inst(inst) => { - let block = self.insts[inst].block; - self.blocks[block].insts().position(|i| i == inst).unwrap() + 1 - } - }; - a_seq.cmp(&b_seq) - } - - pub fn call_signature(&self, inst: Inst) -> Option<&Signature> { - match self.insts[inst].analyze_call(&self.value_lists) { - CallInfo::NotACall => None, - CallInfo::Direct(ref f, _) => Some(&self.imports[f].signature), - } - } - - pub fn analyze_call(&self, inst: Inst) -> CallInfo<'_> { - self.insts[inst].analyze_call(&self.value_lists) - } - - pub fn analyze_branch(&self, inst: Inst) -> BranchInfo { - self.insts[inst].analyze_branch(&self.value_lists) - } - - pub fn blocks(&self) -> impl Iterator { - Blocks { - cursor: self.blocks.cursor(), - } - } - - /// Get the block identifier for the entry block - #[inline(always)] - pub fn entry_block(&self) -> Block { - self.entry - } - - /// Get a reference to the data for the entry block - #[inline] - pub fn entry(&self) -> &BlockData { - &self.blocks[self.entry] - } - - /// Get a mutable reference to the data for the entry block - #[inline] - pub fn entry_mut(&mut self) -> &mut BlockData { - &mut self.blocks[self.entry] - } - - pub(super) fn last_block(&self) -> Option { - self.blocks.last().map(|b| b.key()) - } - - pub fn num_blocks(&self) -> usize { - self.blocks.iter().count() - } - - /// Get an immutable reference to the block data for `block` - pub fn block(&self, block: Block) -> &BlockData { - &self.blocks[block] - } - - /// Get a mutable reference to the block data for `block` - pub fn block_mut(&mut self, block: Block) -> &mut BlockData { - &mut self.blocks[block] - } - - pub fn block_args(&self, block: Block) -> &[Value] { - self.blocks[block].params.as_slice(&self.value_lists) - } - - pub fn block_insts(&self, block: Block) -> impl Iterator + '_ { - self.blocks[block].insts() - } - - pub fn last_inst(&self, block: Block) -> Option { - self.blocks[block].last() - } - - pub fn is_block_linked(&self, block: Block) -> bool { - self.blocks.contains(block) - } - - pub fn is_block_empty(&self, block: Block) -> bool { - self.blocks[block].is_empty() - } - - pub fn create_block(&mut self) -> Block { - let id = self.blocks.create(); - let data = BlockData::new(id); - self.blocks.append(id, data); - id - } - - pub fn append_block(&mut self, block: Block) { - self.blocks.append(block, BlockData::new(block)); - } - - /// Creates a new block, inserted into the function layout just after `block` - pub fn create_block_after(&mut self, block: Block) -> Block { - let id = self.blocks.create(); - let data = BlockData::new(id); - self.blocks.insert_after(id, block, data); - id - } - - /// Removes `block` from the body of this function, without destroying it's data - pub fn detach_block(&mut self, block: Block) { - self.blocks.remove(block); - } - - pub fn num_block_params(&self, block: Block) -> usize { - self.blocks[block].params.len(&self.value_lists) - } - - pub fn block_params(&self, block: Block) -> &[Value] { - self.blocks[block].params.as_slice(&self.value_lists) - } - - pub fn block_param(&self, block: Block, index: usize) -> &ValueData { - self.blocks[block] - .params - .get(index, &self.value_lists) - .map(|id| self.value_data(id)) - .expect("block argument index is out of bounds") - } - - pub fn block_param_types(&self, block: Block) -> SmallVec<[Type; 1]> { - self.block_params(block) - .iter() - .map(|&v| self.value_type(v).clone()) - .collect() - } - - /// Clone the block parameters of `src` as a new set of values, derived from the data used to - /// crate the originals, and use them to populate the block arguments of `dest`, in the same - /// order. - pub fn clone_block_params(&mut self, src: Block, dest: Block) { - debug_assert_eq!( - self.num_block_params(dest), - 0, - "cannot clone block params to a block that already has params" - ); - let num_params = self.num_block_params(src); - for i in 0..num_params { - let value = self.block_param(src, i); - let ty = value.ty().clone(); - let span = value.span(); - self.append_block_param(dest, ty, span); - } - } - - pub fn append_block_param(&mut self, block: Block, ty: Type, span: SourceSpan) -> Value { - let param = self.values.next_key(); - let num = self.blocks[block].params.push(param, &mut self.value_lists); - debug_assert!(num <= u16::MAX as usize, "too many parameters on block"); - self.make_value(ValueData::Param { - ty, - num: num as u16, - block, - span, - }) - } - - pub fn is_block_terminated(&self, block: Block) -> bool { - if let Some(inst) = self.last_inst(block) { - self.inst(inst).opcode().is_terminator() - } else { - false - } - } - - /// Removes `val` from `block`'s parameters by a standard linear time list removal which - /// preserves ordering. Also updates the values' data. - pub fn remove_block_param(&mut self, val: Value) { - let (block, num) = if let ValueData::Param { num, block, .. } = self.values[val] { - (block, num) - } else { - panic!("{} must be a block parameter", val); - }; - self.blocks[block] - .params - .remove(num as usize, &mut self.value_lists); - for index in num..(self.num_block_params(block) as u16) { - let value_data = &mut self.values[self.blocks[block] - .params - .get(index as usize, &self.value_lists) - .unwrap()]; - let mut value_data_clone = value_data.clone(); - match &mut value_data_clone { - ValueData::Param { ref mut num, .. } => { - *num -= 1; - *value_data = value_data_clone; - } - _ => panic!( - "{} must be a block parameter", - self.blocks[block] - .params - .get(index as usize, &self.value_lists) - .unwrap() - ), - } - } - } - - /// Appends `value` as an argument to the `branch_inst` instruction arguments list if the - /// destination block of the `branch_inst` is `dest`. - /// Panics if `branch_inst` is not a branch instruction. - pub fn append_branch_destination_argument( - &mut self, - branch_inst: Inst, - dest: Block, - value: Value, - ) { - match self.insts[branch_inst].data.item { - Instruction::Br(Br { - destination, - ref mut args, - .. - }) if destination == dest => { - 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), - .. - }) => { - if then_dest == dest { - then_args.push(value, &mut self.value_lists); - } else if else_dest == dest { - else_args.push(value, &mut self.value_lists); - } - } - Instruction::Switch(Switch { - op: _, - arg: _, - arms: _, - default: _, - }) => { - panic!("cannot append argument {value} to Switch destination block {dest}, since it has no block arguments support"); - } - _ => panic!("{} must be a branch instruction", branch_inst), - } - } -} -impl Index for DataFlowGraph { - type Output = Instruction; - - fn index(&self, inst: Inst) -> &Self::Output { - &self.insts[inst] - } -} -impl IndexMut for DataFlowGraph { - fn index_mut(&mut self, inst: Inst) -> &mut Self::Output { - &mut self.insts[inst] - } -} - -struct Blocks<'f> { - cursor: intrusive_collections::linked_list::Cursor<'f, LayoutAdapter>, -} -impl<'f> Iterator for Blocks<'f> { - type Item = (Block, &'f BlockData); - - fn next(&mut self) -> Option { - if self.cursor.is_null() { - return None; - } - let next = self.cursor.get().map(|data| (data.key(), data.value())); - self.cursor.move_next(); - next - } -} diff --git a/hir/src/display.rs b/hir/src/display.rs deleted file mode 100644 index fce2daff0..000000000 --- a/hir/src/display.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::{cell::Cell, fmt}; - -use super::{Block, Inst}; - -/// This trait is used to decorate the textual formatting of blocks and instructions -/// with additional information, e.g liveness. -pub trait Decorator { - type Display<'a>: fmt::Display - where - Self: 'a; - - /// Emit no decoration for this block when true - fn skip_block(&self, _block: Block) -> bool { - false - } - /// Emit no decoration for this instruction when true - fn skip_inst(&self, _inst: Inst) -> bool { - false - } - /// Emit decoration for `block` by returning a displayable object - fn decorate_block<'a, 'd: 'a>(&'d self, block: Block) -> Self::Display<'a>; - /// Emit decoration for `inst` by returning a displayable object - fn decorate_inst<'a, 'd: 'a>(&'d self, inst: Inst) -> Self::Display<'a>; -} -impl Decorator for () { - type Display<'a> = &'a str; - - fn skip_block(&self, _block: Block) -> bool { - true - } - fn skip_inst(&self, _inst: Inst) -> bool { - true - } - fn decorate_block<'a, 'd: 'a>(&'d self, _block: Block) -> Self::Display<'a> { - "" - } - fn decorate_inst<'a, 'd: 'a>(&'d self, _inst: Inst) -> Self::Display<'a> { - "" - } -} - -/// Render an iterator of `T`, comma-separated -pub struct DisplayValues(Cell>); -impl DisplayValues { - pub fn new(inner: T) -> Self { - Self(Cell::new(Some(inner))) - } -} -impl fmt::Display for DisplayValues -where - T: fmt::Display, - I: Iterator, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let iter = self.0.take().unwrap(); - for (i, item) in iter.enumerate() { - if i == 0 { - write!(f, "{}", item)?; - } else { - write!(f, ", {}", item)?; - } - } - Ok(()) - } -} - -/// Render an `Option` using the `Display` impl for `T` -pub struct DisplayOptional<'a, T>(pub Option<&'a T>); -impl<'a, T: fmt::Display> fmt::Display for DisplayOptional<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { - None => f.write_str("None"), - Some(item) => write!(f, "Some({item})"), - } - } -} -impl<'a, T: fmt::Display> fmt::Debug for DisplayOptional<'a, T> { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} diff --git a/hir/src/function.rs b/hir/src/function.rs deleted file mode 100644 index 040e3e016..000000000 --- a/hir/src/function.rs +++ /dev/null @@ -1,464 +0,0 @@ -use std::fmt; - -use cranelift_entity::entity_impl; -use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink}; -use miden_diagnostics::Spanned; - -use super::{pass::AnalysisKey, *}; - -/// 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")] -pub struct SymbolConflictError(pub FunctionIdent); - -/// A handle that refers to an [ExternalFunction] -#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct FuncRef(u32); -entity_impl!(FuncRef, "fn"); - -/// Represents the calling convention of a function. -/// -/// Calling conventions are part of a program's ABI (Application Binary Interface), and -/// they define things such how arguments are passed to a function, how results are returned, -/// etc. In essence, the contract between caller and callee is described by the calling convention -/// of a function. -/// -/// Importantly, it is perfectly normal to mix calling conventions. For example, the public -/// API for a C library will use whatever calling convention is used by C on the target -/// platform (for Miden, that would be `SystemV`). However, internally that library may use -/// the `Fast` calling convention to allow the compiler to optimize more effectively calls -/// from the public API to private functions. In short, choose a calling convention that is -/// well-suited for a given function, to the extent that other constraints don't impose a choice -/// on you. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] -pub enum CallConv { - /// This calling convention is what I like to call "chef's choice" - the - /// compiler chooses it's own convention that optimizes for call performance. - /// - /// As a result of this, it is not permitted to use this convention in externally - /// linked functions, as the convention is unstable, and the compiler can't ensure - /// that the caller in another translation unit will use the correct convention. - Fast, - /// The standard calling convention used for C on most platforms - #[default] - SystemV, - /// A function with this calling convention must be called using - /// the `syscall` instruction. Attempts to call it with any other - /// call instruction will cause a validation error. The one exception - /// to this rule is when calling another function with the `Kernel` - /// convention that is defined in the same module, which can use the - /// standard `call` instruction. - /// - /// Kernel functions may only be defined in a kernel [Module]. - /// - /// In all other respects, this calling convention is the same as `SystemV` - Kernel, -} -impl fmt::Display for CallConv { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Fast => f.write_str("fast"), - Self::SystemV => f.write_str("C"), - Self::Kernel => f.write_str("kernel"), - } - } -} - -/// Represents whether an argument or return value has a special purpose in -/// the calling convention of a function. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] -pub enum ArgumentPurpose { - /// No special purpose, the argument is passed/returned by value - #[default] - Default, - /// Used for platforms where the calling convention expects return values of - /// a certain size to be written to a pointer passed in by the caller. - StructReturn, -} -impl fmt::Display for ArgumentPurpose { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Default => f.write_str("default"), - Self::StructReturn => f.write_str("sret"), - } - } -} - -/// Represents how to extend a small integer value to native machine integer width. -/// -/// For Miden, native integrals are unsigned 64-bit field elements, but it is typically -/// going to be the case that we are targeting the subset of Miden Assembly where integrals -/// are unsigned 32-bit integers with a standard twos-complement binary representation. -/// -/// It is for the latter scenario that argument extension is really relevant. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] -pub enum ArgumentExtension { - /// Do not perform any extension, high bits have undefined contents - #[default] - None, - /// Zero-extend the value - Zext, - /// Sign-extend the value - Sext, -} -impl fmt::Display for ArgumentExtension { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::None => f.write_str("none"), - Self::Zext => f.write_str("zext"), - Self::Sext => f.write_str("sext"), - } - } -} - -/// Describes a function parameter or result. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AbiParam { - /// The type associated with this value - pub ty: Type, - /// The special purpose, if any, of this parameter or result - pub purpose: ArgumentPurpose, - /// The desired approach to extending the size of this value to - /// a larger bit width, if applicable. - pub extension: ArgumentExtension, -} -impl AbiParam { - pub fn new(ty: Type) -> Self { - Self { - ty, - purpose: ArgumentPurpose::default(), - extension: ArgumentExtension::default(), - } - } - - pub fn sret(ty: Type) -> Self { - assert!(ty.is_pointer(), "sret parameters must be pointers"); - Self { - ty, - purpose: ArgumentPurpose::StructReturn, - extension: ArgumentExtension::default(), - } - } -} - -/// A [Signature] represents the type, ABI, and linkage of a function. -/// -/// A function signature provides us with all of the necessary detail to correctly -/// validate and emit code for a function, whether from the perspective of a caller, -/// or the callee. -#[derive(Debug, Clone)] -pub struct Signature { - /// The arguments expected by this function - pub params: Vec, - /// The results returned by this function - pub results: Vec, - /// The calling convention that applies to this function - pub cc: CallConv, - /// The linkage that should be used for this function - pub linkage: Linkage, -} -impl Signature { - /// Create a new signature with the given parameter and result types, - /// for a public function using the `SystemV` calling convention - pub fn new, R: IntoIterator>( - params: P, - results: R, - ) -> Self { - Self { - params: params.into_iter().collect(), - results: results.into_iter().collect(), - cc: CallConv::SystemV, - linkage: Linkage::External, - } - } - - /// Returns true if this function is externally visible - pub fn is_public(&self) -> bool { - matches!(self.linkage, Linkage::External) - } - - /// Returns true if this function is only visible within it's containing module - pub fn is_private(&self) -> bool { - matches!(self.linkage, Linkage::Internal) - } - - /// Returns true if this function is a kernel function - pub fn is_kernel(&self) -> bool { - matches!(self.cc, CallConv::Kernel) - } - - /// Returns the number of arguments expected by this function - pub fn arity(&self) -> usize { - self.params().len() - } - - /// Returns a slice containing the parameters for this function - pub fn params(&self) -> &[AbiParam] { - self.params.as_slice() - } - - /// Returns the parameter at `index`, if present - #[inline] - pub fn param(&self, index: usize) -> Option<&AbiParam> { - self.params.get(index) - } - - /// Returns a slice containing the results of this function - pub fn results(&self) -> &[AbiParam] { - match self.results.as_slice() { - [AbiParam { ty: Type::Unit, .. }] => &[], - [AbiParam { - ty: Type::Never, .. - }] => &[], - results => results, - } - } -} -impl Eq for Signature {} -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - self.linkage == other.linkage - && self.cc == other.cc - && self.params.len() == other.params.len() - && self.results.len() == other.results.len() - } -} - -/// An [ExternalFunction] represents a function whose name and signature are known, -/// but which may or may not be compiled as part of the current translation unit. -/// -/// When building a [Function], we use [ExternalFunction] to represent references to -/// other functions in the program which are called from its body. One "imports" a -/// function to make it callable. -/// -/// At link time, we make sure all external function references are either defined in -/// the current program, or are well-known functions that are provided as part of a kernel -/// or standard library in the Miden VM. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ExternalFunction { - pub id: FunctionIdent, - pub signature: Signature, -} -impl Ord for ExternalFunction { - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.id.cmp(&other.id) - } -} -impl PartialOrd for ExternalFunction { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -intrusive_adapter!(pub FunctionListAdapter = Box: Function { link: LinkedListLink }); - -/// A type alias for `LinkedList` -pub type FunctionList = LinkedList; - -/// [Function] corresponds to a function definition, in single-static assignment (SSA) form. -/// -/// * Functions may have zero or more parameters, and produce zero or more results. -/// * Functions are namespaced in [Module]s. You may define a function separately from a module, -/// to aid in parallelizing compilation, but functions must be attached to a module prior to code -/// generation. Furthermore, in order to reference other functions, you must do so using their -/// fully-qualified names. -/// * Functions consist of one or more basic blocks, where the entry block is predefined based -/// on the function signature. -/// * Basic blocks consist of a sequence of [Instruction] without any control flow (excluding calls), -/// terminating with a control flow instruction. Our SSA representation uses block arguments rather -/// than phi nodes to represent join points in the control flow graph. -/// * Instructions consume zero or more arguments, and produce zero or more results. Results produced -/// by an instruction constitute definitions of those values. A value may only ever have a single -/// definition, e.g. you can't reassign a value after it is introduced by an instruction. -/// -/// References to functions and global variables from a [Function] are not fully validated until -/// link-time/code generation. -#[derive(Spanned, AnalysisKey)] -pub struct Function { - link: LinkedListLink, - #[span] - #[analysis_key] - pub id: FunctionIdent, - pub signature: Signature, - pub dfg: DataFlowGraph, -} -impl Function { - /// Create a new [Function] with the given name, signature, and source location. - /// - /// The resulting function will be given default internal linkage, i.e. it will only - /// be visible within it's containing [Module]. - pub fn new(id: FunctionIdent, signature: Signature) -> Self { - let mut dfg = DataFlowGraph::default(); - let entry = dfg.entry_block(); - for param in signature.params() { - dfg.append_block_param(entry, param.ty.clone(), id.span()); - } - dfg.imports.insert( - id, - ExternalFunction { - id, - signature: signature.clone(), - }, - ); - Self { - link: Default::default(), - id, - signature, - dfg, - } - } - - /// This function is like [Function::new], except it does not initialize the - /// function entry block using the provided [Signature]. Instead, it is expected - /// that the caller does this manually. - /// - /// This is primarily intended for use by the IR parser. - pub(crate) fn new_uninit(id: FunctionIdent, signature: Signature) -> Self { - let mut dfg = DataFlowGraph::new_uninit(); - dfg.imports.insert( - id, - ExternalFunction { - id, - signature: signature.clone(), - }, - ); - Self { - link: Default::default(), - id, - signature, - dfg, - } - } - - /// Returns true if this function has yet to be attached to a [Module] - pub fn is_detached(&self) -> bool { - !self.link.is_linked() - } - - /// Returns true if this function is a kernel function - pub fn is_kernel(&self) -> bool { - self.signature.is_kernel() - } - - /// Returns true if this function has external linkage - pub fn is_public(&self) -> bool { - self.signature.is_public() - } - - /// Return the [Signature] for this function - #[inline] - pub fn signature(&self) -> &Signature { - &self.signature - } - - /// Return the [Signature] for this function - #[inline] - pub fn signature_mut(&mut self) -> &mut Signature { - &mut self.signature - } - - /// Return the number of parameters this function expects - pub fn arity(&self) -> usize { - self.signature.arity() - } - - /// Return the [Linkage] type for this function - pub fn linkage(&self) -> Linkage { - self.signature.linkage - } - - /// Set the linkage type for this function - pub fn set_linkage(&mut self, linkage: Linkage) { - self.signature.linkage = linkage; - } - - /// Return the [CallConv] type for this function - pub fn calling_convention(&self) -> CallConv { - self.signature.cc - } - - /// Set the linkage type for this function - pub fn set_calling_convention(&mut self, cc: CallConv) { - self.signature.cc = cc; - } - - /// Return true if this function has attribute `name` - pub fn has_attribute(&self, name: &Q) -> bool - where - Q: Ord + ?Sized, - Symbol: std::borrow::Borrow, - { - self.dfg.has_attribute(name) - } - - /// Iterate over all of the external functions imported by this function - pub fn imports<'a, 'b: 'a>(&'b self) -> impl Iterator + 'a { - self.dfg.imports().filter(|ext| ext.id != self.id) - } - - pub fn builder(&mut self) -> FunctionBuilder { - FunctionBuilder::new(self) - } -} -impl fmt::Debug for Function { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Function") - .field("id", &self.id) - .field("signature", &self.signature) - .finish() - } -} -impl fmt::Display for Function { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - crate::write_function(f, self) - } -} -impl Eq for Function {} -impl PartialEq for Function { - fn eq(&self, other: &Self) -> bool { - let is_eq = self.id == other.id && self.signature == other.signature; - if !is_eq { - return false; - } - - // We expect the entry block to be the same - if self.dfg.entry != other.dfg.entry { - return false; - } - - // We expect the blocks to be laid out in the same order, and to have the same parameter lists - for (block_id, block) in self.dfg.blocks() { - if let Some(other_block) = other.dfg.blocks.get(block_id) { - if block.params.as_slice(&self.dfg.value_lists) - != other_block.params.as_slice(&other.dfg.value_lists) - { - return false; - } - // We expect the instructions in each block to be the same - if !block - .insts - .iter() - .map(|i| InstructionWithValueListPool { - inst: i, - value_lists: &self.dfg.value_lists, - }) - .eq(other_block - .insts - .iter() - .map(|i| InstructionWithValueListPool { - inst: i, - value_lists: &other.dfg.value_lists, - })) - { - return false; - } - } else { - return false; - } - } - - // We expect both functions to have the same imports - self.dfg.imports == other.dfg.imports - } -} diff --git a/hir/src/globals.rs b/hir/src/globals.rs deleted file mode 100644 index 166f65fa2..000000000 --- a/hir/src/globals.rs +++ /dev/null @@ -1,693 +0,0 @@ -use std::alloc::Layout; -use std::collections::{hash_map::DefaultHasher, BTreeMap}; -use std::fmt::{self, Write}; -use std::hash::{Hash, Hasher}; - -use cranelift_entity::entity_impl; -use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink, UnsafeRef}; -use miden_diagnostics::Spanned; - -use super::*; - -/// The policy to apply to a global variable (or function) when linking -/// together a program during code generation. -/// -/// Miden doesn't (currently) have a notion of a symbol table for things like global variables. -/// At runtime, there are not actually symbols at all in any familiar sense, instead functions, -/// being the only entities with a formal identity in MASM, are either inlined at all their call -/// sites, or are referenced by the hash of their MAST root, to be unhashed at runtime if the call -/// is executed. -/// -/// Because of this, and because we cannot perform linking ourselves (we must emit separate modules, -/// and leave it up to the VM to link them into the MAST), there are limits to what we can do in -/// terms of linking function symbols. We essentially just validate that given a set of modules in -/// a [Program], that there are no invalid references across modules to symbols which either don't -/// exist, or which exist, but have internal linkage. -/// -/// However, with global variables, we have a bit more freedom, as it is a concept that we are -/// completely inventing from whole cloth without explicit support from the VM or Miden Assembly. -/// In short, when we compile a [Program] to MASM, we first gather together all of the global variables -/// into a program-wide table, merging and garbage collecting as appropriate, and updating all -/// references to them in each module. This global variable table is then assumed to be laid out -/// in memory starting at the base of the linear memory address space in the same order, with appropriate -/// padding to ensure accesses are aligned. Then, when emitting MASM instructions which reference -/// global values, we use the layout information to derive the address where that global value -/// is allocated. -/// -/// This has some downsides however, the biggest of which is that we can't prevent someone from -/// loading modules generated from a [Program] with either their own hand-written modules, or -/// even with modules from another [Program]. In such cases, assumptions about the allocation of -/// linear memory from different sets of modules will almost certainly lead to undefined behavior. -/// In the future, we hope to have a better solution to this problem, preferably one involving -/// native support from the Miden VM itself. For now though, we're working with what we've got. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] -pub enum Linkage { - /// This symbol is only visible in the containing module. - /// - /// Internal symbols may be renamed to avoid collisions - /// - /// Unreferenced internal symbols can be discarded at link time. - Internal, - /// This symbol will be linked using the "one definition rule", i.e. symbols with - /// the same name, type, and linkage will be merged into a single definition. - /// - /// Unlike `internal` linkage, unreferenced `odr` symbols cannot be discarded. - /// - /// NOTE: `odr` symbols cannot satisfy external symbol references - Odr, - /// This symbol is visible externally, and can be used to resolve external symbol references. - #[default] - External, -} -impl fmt::Display for Linkage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Internal => f.write_str("internal"), - Self::Odr => f.write_str("odr"), - Self::External => f.write_str("external"), - } - } -} - -intrusive_adapter!(pub GlobalVariableAdapter = UnsafeRef: GlobalVariableData { link: LinkedListLink }); - -/// This error is raised when attempting to declare [GlobalVariableData] -/// with a conflicting symbol name and/or linkage. -/// -/// 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)] -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}'" - )] - 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")] - 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" - )] - InvalidInit(Ident), -} - -/// Describes the way in which global variable conflicts will be handled -#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] -pub enum ConflictResolutionStrategy { - /// Do not attempt to resolve conflicts - /// - /// NOTE: This does not change the behavior of "one definition rule" linkage, - /// when the globals have identical definitions. - None, - /// Attempt to resolve conflicts by renaming symbols with `internal` linkage. - #[default] - Rename, -} - -/// This table is used to lay out and link together global variables for a [Program]. -/// -/// See the docs for [Linkage], [GlobalVariableData], and [GlobalVariableTable::declare] for more details. -pub struct GlobalVariableTable { - layout: LinkedList, - names: BTreeMap, - arena: ArenaMap, - data: ConstantPool, - next_unique_id: usize, - conflict_strategy: ConflictResolutionStrategy, -} -impl Default for GlobalVariableTable { - fn default() -> Self { - Self::new(Default::default()) - } -} -impl GlobalVariableTable { - pub fn new(conflict_strategy: ConflictResolutionStrategy) -> Self { - Self { - layout: Default::default(), - names: Default::default(), - arena: Default::default(), - data: ConstantPool::default(), - next_unique_id: 0, - conflict_strategy, - } - } - - /// Returns the number of global variables in this table - pub fn len(&self) -> usize { - self.layout.iter().count() - } - - /// Returns true if the global variable table is empty - pub fn is_empty(&self) -> bool { - self.layout.is_empty() - } - - /// Get a double-ended iterator over the current table layout - pub fn iter<'a, 'b: 'a>( - &'b self, - ) -> intrusive_collections::linked_list::Iter<'a, GlobalVariableAdapter> { - self.layout.iter() - } - - /// Returns true if a global variable with `name` has been declared - pub fn exists(&self, name: Ident) -> bool { - self.names.contains_key(&name) - } - - /// Looks up a [GlobalVariable] by name. - pub fn find(&self, name: Ident) -> Option { - self.names.get(&name).copied() - } - - /// Gets the data associated with the given [GlobalVariable] - pub fn get(&self, id: GlobalVariable) -> &GlobalVariableData { - &self.arena[id] - } - - /// Checks if the given `id` can be found in this table - pub fn contains_key(&self, id: GlobalVariable) -> bool { - self.arena.contains(id) - } - - /// Removes the global variable associated with `id` from this table - /// - /// The actual definition remains behind, in order to ensure that `id` - /// remains valid should there be any other outstanding references, - /// however the data is removed from the layout, and will not be - /// seen when traversing the table. - pub fn remove(&mut self, id: GlobalVariable) { - let mut cursor = self.layout.front_mut(); - while let Some(gv) = cursor.get() { - if gv.id == id { - cursor.remove(); - return; - } - - cursor.move_next(); - } - } - - /// Computes the total size in bytes of the table, as it is currently laid out. - pub fn size_in_bytes(&self) -> usize { - // We mimic the allocation process here, by visiting each - // global variable, padding the current heap pointer as necessary - // to provide the necessary minimum alignment for the value, and - // then bumping it by the size of the value itself. - // - // At the end, the effective address of the pointer is the total - // size in bytes of the allocation - let mut size = 0; - for gv in self.layout.iter() { - let layout = gv.layout(); - size += layout.size().align_up(layout.align()); - } - size - } - - /// Computes the offset, in bytes, of the given [GlobalVariable] from the - /// start of the segment in which globals are allocated, assuming that the - /// layout of the global variable table up to and including `id` remains - /// unchanged. - /// - /// # Safety - /// - /// This should only be used once all data segments and global variables have - /// been declared, and the layout of the table has been decided. It is technically - /// safe to use offsets obtained before all global variables are declared, _IF_ the - /// data segments and global variable layout up to and including those global variables - /// remains unchanged after that point. - /// - /// If the offset for a given global variable is obtained, and the heap layout is - /// subsequently changed in such a way that the original offset is no longer - /// accurate, bad things will happen. - pub unsafe fn offset_of(&self, id: GlobalVariable) -> u32 { - let mut size = 0usize; - for gv in self.layout.iter() { - let layout = gv.layout(); - let align_offset = layout.size().align_offset(layout.align()); - size += align_offset; - - // If the current variable is the one we're after, - // the aligned address is the offset to the start - // of the allocation, so we're done - if gv.id == id { - break; - } - - size += layout.size(); - } - size.try_into().expect("data segment table is invalid") - } - - /// Get the constant data associated with `id` - pub fn get_constant(&self, id: Constant) -> &ConstantData { - self.data.get(id) - } - - /// Inserts the given constant data into this table without allocating a global - pub fn insert_constant(&mut self, data: ConstantData) -> Constant { - self.data.insert(data) - } - - /// Returns true if the given constant data is in the constant pool - pub fn contains_constant(&self, data: &ConstantData) -> bool { - self.data.contains(data) - } - - /// Traverse all of the constants in the table - #[inline] - pub fn constants(&self) -> impl Iterator { - self.data.iter() - } - - /// Returns true if the table has constant data stored - pub fn has_constants(&self) -> bool { - !self.data.is_empty() - } - - /// Declares a new global variable with the given symbol name, type, linkage, and optional initializer. - /// - /// If successful, `Ok` is returned, with the [GlobalVariable] corresponding to the data for the symbol. - /// - /// Returns an error if the specification of the global is invalid in any way, or the declaration conflicts - /// with a previous declaration of the same name. - /// - /// NOTE: While similar to `try_insert`, a key difference is that `try_declare` does not attempt to resolve - /// conflicts. If the given name has been previously declared, and the declarations are not identical, - /// then an error will be returned. This is because conflict resolution is a process performed when linking - /// together modules. Declaring globals is done during the initial construction of a module, where any - /// attempt to rename a global variable locally would cause unexpected issues as references to that global - /// are emitted. Once a module is constructed, globals it declares with internal linkage can be renamed freely, - /// as the name is no longer significant. - pub fn declare( - &mut self, - name: Ident, - ty: Type, - linkage: Linkage, - init: Option, - ) -> Result { - assert_ne!( - name.as_symbol(), - symbols::Empty, - "global variable declarations require a non-empty symbol name" - ); - - // Validate the initializer - let init = match init { - None => None, - Some(init) => { - let layout = ty.layout(); - if init.len() > layout.size() { - return Err(GlobalVariableError::InvalidInit(name)); - } - Some(self.data.insert(init)) - } - }; - - let data = GlobalVariableData { - link: Default::default(), - id: Default::default(), - name, - ty, - linkage, - init, - }; - - // If the symbol is already declared, but the declarations are compatible, then - // return the id of the existing declaration. If the declarations are incompatible, - // then we raise an error. - // - // If the symbol is not declared yet, proceed with insertion. - if let Some(gv) = self.names.get(&data.name).copied() { - if data.is_compatible_with(&self.arena[gv]) { - // If the declarations are compatible, and the new declaration has an initializer, - // then the previous declaration must either have no initializer, or the same one, - // but we want to make sure that the initializer is set if not already. - if data.init.is_some() { - self.arena[gv].init = data.init; - } - Ok(gv) - } else { - Err(GlobalVariableError::NameConflict(data.name)) - } - } else { - Ok(unsafe { self.insert(data) }) - } - } - - /// Attempt to insert the given [GlobalVariableData] into this table. - /// - /// Returns the id of the global variable in the table, along with a flag indicating whether the global symbol - /// was renamed to resolve a conflict with an existing symbol. The caller is expected to handle such renames - /// so that any references to the original name that are affected can be updated. - /// - /// If there was an unresolvable conflict, an error will be returned. - pub fn try_insert( - &mut self, - mut data: GlobalVariableData, - ) -> Result<(GlobalVariable, bool), GlobalVariableError> { - assert_ne!( - data.name.as_symbol(), - symbols::Empty, - "global variable declarations require a non-empty symbol name" - ); - - if let Some(gv) = self.names.get(&data.name).copied() { - // The symbol is already declared, check to see if they are compatible - if data.is_compatible_with(&self.arena[gv]) { - // If the declarations are compatible, and the new declaration has an initializer, - // then the previous declaration must either have no initializer, or the same one, - // but we make sure that the initializer is set. - if data.init.is_some() { - self.arena[gv].init = data.init; - } - return Ok((gv, false)); - } - - // Otherwise, the declarations conflict, but depending on the conflict resolution - // strategy, we may yet be able to proceed. - let rename_internal_symbols = - matches!(self.conflict_strategy, ConflictResolutionStrategy::Rename); - match data.linkage { - // Conflicting declarations with internal linkage can be resolved by renaming - Linkage::Internal if rename_internal_symbols => { - let mut generated = String::from(data.name.as_str()); - let original_len = generated.len(); - loop { - // Allocate a new unique integer value to mix into the hash - 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(); - data.hash(&mut hasher); - unique_id.hash(&mut hasher); - let hash = hasher.finish(); - // Append `.` as a suffix to the original symbol name - write!(&mut generated, ".{:x}", hash) - .expect("failed to write unique suffix to global variable name"); - // If by some stroke of bad luck we generate a symbol name that - // is in use, try again with a different unique id until we find - // an unused name - if !self.names.contains_key(generated.as_str()) { - data.name = - Ident::new(Symbol::intern(generated.as_str()), data.name.span()); - break; - } - // Strip off the suffix we just added before we try again - generated.truncate(original_len); - } - - let gv = unsafe { self.insert(data) }; - Ok((gv, true)) - } - // In all other cases, a conflicting declaration cannot be resolved - Linkage::External | Linkage::Internal | Linkage::Odr => { - Err(GlobalVariableError::NameConflict(data.name)) - } - } - } else { - let gv = unsafe { self.insert(data) }; - Ok((gv, false)) - } - } - - /// This sets the initializer for the given [GlobalVariable] to `init`. - /// - /// This function will return `Err` if any of the following occur: - /// - /// * The global variable already has an initializer - /// * The given data does not match the type of the global variable, i.e. more data than the type supports. - /// - /// If the data is smaller than the type of the global variable, the data will be zero-extended to fill it out. - /// - /// NOTE: The initializer data is expected to be in little-endian order. - pub fn set_initializer( - &mut self, - gv: GlobalVariable, - init: ConstantData, - ) -> Result<(), GlobalVariableError> { - let global = &mut self.arena[gv]; - let layout = global.layout(); - if init.len() > layout.size() { - return Err(GlobalVariableError::InvalidInit(global.name)); - } - - match global.init { - // If the global is uninitialized, we're good to go - None => { - global.init = Some(self.data.insert(init)); - } - // If it is already initialized, but the initializers are the - // same, then we consider this a successful, albeit redundant, - // operation; otherwise we raise an error. - Some(prev_init) => { - let prev = self.data.get(prev_init); - if prev != &init { - return Err(GlobalVariableError::AlreadyInitialized(global.name)); - } - } - } - - Ok(()) - } - - /// This is a low-level operation to insert global variable data directly into the table, allocating - /// a fresh unique id, which is then returned. - /// - /// # SAFETY - /// - /// It is expected that the caller has already guaranteed that the name of the given global variable - /// is not present in the table, and that all validation rules for global variables have been enforced. - pub(crate) unsafe fn insert(&mut self, mut data: GlobalVariableData) -> GlobalVariable { - let name = data.name; - // Allocate the data in the arena - let gv = if data.id == GlobalVariable::default() { - let gv = self.arena.alloc_key(); - data.id = gv; - gv - } else { - data.id - }; - self.arena.append(gv, data); - // Add the symbol name to the symbol map - self.names.insert(name, gv); - - // Add the global variable to the layout - let unsafe_ref = unsafe { - let ptr = self.arena.get_raw(gv).unwrap(); - UnsafeRef::from_raw(ptr.as_ptr()) - }; - self.layout.push_back(unsafe_ref); - gv - } -} -impl fmt::Debug for GlobalVariableTable { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.layout.iter()).finish() - } -} - -/// A handle to a global variable definition -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct GlobalVariable(u32); -entity_impl!(GlobalVariable, "gvar"); -impl Default for GlobalVariable { - #[inline] - fn default() -> Self { - use cranelift_entity::packed_option::ReservedValue; - - Self::reserved_value() - } -} - -/// A [GlobalVariable] represents a concrete definition for a symbolic value, -/// i.e. it corresponds to the actual allocated memory referenced by a [GlobalValueData::Symbol] -/// value. -#[derive(Clone)] -pub struct GlobalVariableData { - /// The intrusive link used for storing this global variable in a list - link: LinkedListLink, - /// The unique identifier associated with this global variable - id: GlobalVariable, - /// The symbol name for this global variable - pub name: Ident, - /// The type of the value this variable is allocated for. - /// - /// Nothing prevents one from accessing the variable as if it is - /// another type, but at a minimum this type is used to derive the - /// size and alignment requirements for this global variable on - /// the heap. - pub ty: Type, - /// The linkage for this global variable - pub linkage: Linkage, - /// The initializer for this global variable, if applicable - pub init: Option, -} -impl GlobalVariableData { - pub(crate) fn new( - id: GlobalVariable, - name: Ident, - ty: Type, - linkage: Linkage, - init: Option, - ) -> Self { - Self { - link: LinkedListLink::new(), - id, - name, - ty, - linkage, - init, - } - } - - /// Get the unique identifier assigned to this global variable - #[inline] - pub fn id(&self) -> GlobalVariable { - self.id - } - - /// Return the [Layout] of this global variable in memory - pub fn layout(&self) -> Layout { - self.ty.layout() - } - - /// Return a handle to the initializer for this global variable, if present - pub fn initializer(&self) -> Option { - self.init - } - - /// Returns true if `self` is compatible with `other`, meaning that the two declarations are - /// identical in terms of type and linkage, and do not have conflicting initializers. - /// - /// NOTE: The name of the global is not considered here, only the properties of the value itself. - pub fn is_compatible_with(&self, other: &Self) -> bool { - let compatible_init = - self.init.is_none() || other.init.is_none() || self.init == other.init; - self.ty == other.ty && self.linkage == other.linkage && compatible_init - } -} -impl Eq for GlobalVariableData {} -impl PartialEq for GlobalVariableData { - fn eq(&self, other: &Self) -> bool { - self.linkage == other.linkage - && self.ty == other.ty - && self.name == other.name - && self.init == other.init - } -} -impl Hash for GlobalVariableData { - fn hash(&self, state: &mut H) { - self.name.hash(state); - self.ty.hash(state); - self.linkage.hash(state); - self.init.hash(state); - } -} -impl fmt::Debug for GlobalVariableData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("GlobalVariableData") - .field("id", &self.id) - .field("name", &self.name) - .field("ty", &self.ty) - .field("linkage", &self.linkage) - .field("init", &self.init) - .finish() - } -} - -/// A handle to a global variable definition -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct GlobalValue(u32); -entity_impl!(GlobalValue, "gv"); -impl Default for GlobalValue { - #[inline] - fn default() -> Self { - use cranelift_entity::packed_option::ReservedValue; - - Self::reserved_value() - } -} - -/// Data associated with a `GlobalValue`. -/// -/// Globals are allocated statically, and live for the lifetime of the program. -/// In Miden, we allocate globals at the start of the heap. Since all globals are -/// 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. -#[derive(Debug, Clone)] -pub enum GlobalValueData { - /// A symbolic reference to a global variable symbol - /// - /// The type of a symbolic global value is always a pointer, the address - /// of the referenced global variable. - Symbol { - /// The name of the global variable that is referenced - name: Ident, - /// A constant offset, in bytes, from the address of the symbol - offset: i32, - }, - /// A global whose value is given by reading the value from the address - /// derived from another global value and an offset. - Load { - /// The global value whose value is the base pointer - base: GlobalValue, - /// A constant offset, in bytes, from the base address - offset: i32, - /// The type of the value stored at `base + offset` - ty: Type, - }, - /// A global whose value is an address computed as the offset from another global - /// - /// This can be used for `getelementptr`-like situations, such as calculating the - /// address of a field in a struct that is stored in a global variable. - IAddImm { - /// The global value whose value is the base pointer - base: GlobalValue, - /// A constant offset, in units of `ty`, from the base address - offset: i32, - /// The unit type of the offset - /// - /// This can be helpful when computing addresses to elements of an array - /// stored in a global variable. - ty: Type, - }, -} -impl GlobalValueData { - /// Returns true if this global value is a symbolic or computed address - /// which can be resolved at compile-time. - /// - /// Notably, global loads may produce an address, but the value of that - /// address is not known until runtime. - pub fn is_constant_addr(&self) -> bool { - !matches!(self, Self::Load { .. }) - } - - /// Return the computed offset for this global value (relative to it's position in the global table) - pub fn offset(&self) -> i32 { - match self { - Self::Symbol { offset, .. } => *offset, - Self::Load { offset, .. } => *offset, - Self::IAddImm { ref ty, offset, .. } => { - let offset = *offset as usize * ty.size_in_bytes(); - offset - .try_into() - .expect("invalid iadd expression: expected computed offset to fit in i32 range") - } - } - } - - /// Get the type associated with this value, if applicable - pub fn ty(&self) -> Option<&Type> { - match self { - Self::Symbol { .. } => None, - Self::Load { ref ty, .. } => Some(ty), - Self::IAddImm { ref ty, .. } => Some(ty), - } - } -} diff --git a/hir/src/ident.rs b/hir/src/ident.rs deleted file mode 100644 index fe77b032c..000000000 --- a/hir/src/ident.rs +++ /dev/null @@ -1,168 +0,0 @@ -use core::{ - cmp::Ordering, - fmt, - hash::{Hash, Hasher}, - str::FromStr, -}; - -use anyhow::anyhow; -use miden_diagnostics::{SourceSpan, Spanned}; - -use super::{symbols, Symbol}; - -/// Represents a globally-unique module/function name pair, with corresponding source spans. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Spanned)] -pub struct FunctionIdent { - pub module: Ident, - #[span] - pub function: Ident, -} -impl FromStr for FunctionIdent { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - match s.rsplit_once("::") { - Some((ns, id)) => { - let module = Ident::with_empty_span(Symbol::intern(ns)); - let function = Ident::with_empty_span(Symbol::intern(id)); - Ok(Self { - module, - function, - }) - } - None => Err(anyhow!("invalid function name, expected fully-qualified identifier, e.g. 'std::math::u64::checked_add'")), - } - } -} -impl fmt::Debug for FunctionIdent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("FunctionIdent") - .field("module", &self.module.name) - .field("function", &self.function.name) - .finish() - } -} -impl fmt::Display for FunctionIdent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}::{}", &self.module, &self.function) - } -} -impl PartialOrd for FunctionIdent { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for FunctionIdent { - fn cmp(&self, other: &Self) -> Ordering { - self.module - .cmp(&other.module) - .then(self.function.cmp(&other.function)) - } -} - -/// Represents an identifier in the IR. -/// -/// An identifier is some string, along with an associated source span -#[derive(Copy, Clone, Eq, Spanned)] -pub struct Ident { - pub name: Symbol, - #[span] - pub span: SourceSpan, -} -impl Default for Ident { - fn default() -> Self { - Self { - name: symbols::Empty, - span: SourceSpan::UNKNOWN, - } - } -} -impl FromStr for Ident { - type Err = core::convert::Infallible; - - fn from_str(name: &str) -> Result { - Ok(Self::from(name)) - } -} -impl<'a> From<&'a str> for Ident { - fn from(name: &'a str) -> Self { - Self::with_empty_span(Symbol::intern(name)) - } -} -impl Ident { - #[inline] - pub const fn new(name: Symbol, span: SourceSpan) -> Ident { - Ident { name, span } - } - - #[inline] - pub const fn with_empty_span(name: Symbol) -> Ident { - Ident::new(name, SourceSpan::UNKNOWN) - } - - #[inline] - pub fn as_str(self) -> &'static str { - self.name.as_str() - } - - #[inline(always)] - pub fn as_symbol(self) -> Symbol { - self.name - } -} -impl std::borrow::Borrow for Ident { - #[inline] - fn borrow(&self) -> &Symbol { - &self.name - } -} -impl std::borrow::Borrow for Ident { - #[inline] - fn borrow(&self) -> &str { - self.as_str() - } -} -impl Ord for Ident { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.as_str().cmp(other.as_str()) - } -} -impl PartialOrd for Ident { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl PartialEq for Ident { - #[inline] - fn eq(&self, rhs: &Self) -> bool { - self.name == rhs.name - } -} -impl PartialEq for Ident { - #[inline] - fn eq(&self, rhs: &Symbol) -> bool { - self.name.eq(rhs) - } -} -impl PartialEq for Ident { - fn eq(&self, rhs: &str) -> bool { - self.name.as_str() == rhs - } -} -impl Hash for Ident { - fn hash(&self, state: &mut H) { - self.name.hash(state); - } -} -impl fmt::Debug for Ident { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Ident<{} {:?}>", self.name, self.span) - } -} -impl fmt::Display for Ident { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.name, f) - } -} diff --git a/hir/src/immediates.rs b/hir/src/immediates.rs deleted file mode 100644 index 0e0e23ab5..000000000 --- a/hir/src/immediates.rs +++ /dev/null @@ -1,682 +0,0 @@ -use std::{ - fmt, - hash::{Hash, Hasher}, -}; - -use super::{Felt, FieldElement, StarkField, Type}; - -#[derive(Debug, Copy, Clone)] -pub enum Immediate { - I1(bool), - U8(u8), - I8(i8), - U16(u16), - I16(i16), - U32(u32), - I32(i32), - U64(u64), - I64(i64), - I128(i128), - F64(f64), - Felt(Felt), -} -impl Immediate { - pub fn ty(&self) -> Type { - match self { - Self::I1(_) => Type::I1, - Self::U8(_) => Type::U8, - Self::I8(_) => Type::I8, - Self::U16(_) => Type::U16, - Self::I16(_) => Type::I16, - Self::U32(_) => Type::U32, - Self::I32(_) => Type::I32, - Self::U64(_) => Type::U64, - Self::I64(_) => Type::I64, - Self::I128(_) => Type::I128, - Self::F64(_) => Type::F64, - Self::Felt(_) => Type::Felt, - } - } - - /// Returns true if this immediate is a non-negative value - pub fn is_non_negative(&self) -> bool { - match self { - Self::I1(i) => *i, - Self::I8(i) => *i > 0, - Self::U8(i) => *i > 0, - Self::I16(i) => *i > 0, - Self::U16(i) => *i > 0, - Self::I32(i) => *i > 0, - Self::U32(i) => *i > 0, - Self::I64(i) => *i > 0, - Self::U64(i) => *i > 0, - Self::I128(i) => *i > 0, - Self::F64(f) => f.is_sign_positive(), - Self::Felt(_) => true, - } - } - - /// Returns true if this immediate can represent negative values - pub fn is_signed(&self) -> bool { - matches!( - self, - Self::I8(_) | Self::I16(_) | Self::I32(_) | Self::I64(_) | Self::I128(_) | Self::F64(_) - ) - } - - /// Returns true if this immediate can only represent non-negative values - pub fn is_unsigned(&self) -> bool { - matches!( - self, - Self::I1(_) | Self::U8(_) | Self::U16(_) | Self::U32(_) | Self::U64(_) | Self::Felt(_) - ) - } - - /// Returns true if this immediate is an odd integer, otherwise false - /// - /// If the immediate is not an integer, returns `None` - pub fn is_odd(&self) -> Option { - match self { - Self::I1(b) => Some(*b), - Self::U8(i) => Some(*i % 2 == 0), - Self::I8(i) => Some(*i % 2 == 0), - Self::U16(i) => Some(*i % 2 == 0), - Self::I16(i) => Some(*i % 2 == 0), - Self::U32(i) => Some(*i % 2 == 0), - Self::I32(i) => Some(*i % 2 == 0), - Self::U64(i) => Some(*i % 2 == 0), - Self::I64(i) => Some(*i % 2 == 0), - Self::Felt(i) => Some(i.as_int() % 2 == 0), - Self::I128(i) => Some(*i % 2 == 0), - Self::F64(_) => None, - } - } - - /// Returns true if this immediate is a non-zero integer, otherwise false - /// - /// If the immediate is not an integer, returns `None` - pub fn as_bool(self) -> Option { - match self { - Self::I1(b) => Some(b), - Self::U8(i) => Some(i != 0), - Self::I8(i) => Some(i != 0), - Self::U16(i) => Some(i != 0), - Self::I16(i) => Some(i != 0), - Self::U32(i) => Some(i != 0), - Self::I32(i) => Some(i != 0), - Self::U64(i) => Some(i != 0), - Self::I64(i) => Some(i != 0), - Self::Felt(i) => Some(i.as_int() != 0), - Self::I128(i) => Some(i != 0), - Self::F64(_) => None, - } - } - - /// Attempts to convert this value to a u32 - pub fn as_u32(self) -> Option { - match self { - Self::I1(b) => Some(b as u32), - Self::U8(b) => Some(b as u32), - Self::I8(b) if b >= 0 => Some(b as u32), - Self::I8(_) => None, - Self::U16(b) => Some(b as u32), - Self::I16(b) if b >= 0 => Some(b as u32), - Self::I16(_) => None, - Self::U32(b) => Some(b), - Self::I32(b) if b >= 0 => Some(b as u32), - Self::I32(_) => None, - Self::U64(b) => u32::try_from(b).ok(), - Self::I64(b) if b >= 0 => u32::try_from(b as u64).ok(), - Self::I64(_) => None, - Self::Felt(i) => u32::try_from(i.as_int()).ok(), - Self::I128(b) if b >= 0 && b <= (u32::MAX as u64 as i128) => Some(b as u32), - Self::I128(_) => None, - Self::F64(f) => FloatToInt::::to_int(f).ok(), - } - } - - /// Attempts to convert this value to i32 - pub fn as_i32(self) -> Option { - match self { - Self::I1(b) => Some(b as i32), - Self::U8(i) => Some(i as i32), - Self::I8(i) => Some(i as i32), - Self::U16(i) => Some(i as i32), - Self::I16(i) => Some(i as i32), - Self::U32(i) => i.try_into().ok(), - Self::I32(i) => Some(i), - Self::U64(i) => i.try_into().ok(), - Self::I64(i) => i.try_into().ok(), - Self::Felt(i) => i.as_int().try_into().ok(), - Self::I128(i) if i >= (i32::MIN as i128) && i <= (i32::MAX as i128) => Some(i as i32), - Self::I128(_) => None, - Self::F64(f) => FloatToInt::::to_int(f).ok(), - } - } - - /// Attempts to convert this value to a field element - pub fn as_felt(self) -> Option { - match self { - Self::I1(b) => Some(Felt::new(b as u64)), - Self::U8(b) => Some(Felt::new(b as u64)), - Self::I8(b) => u64::try_from(b).ok().map(Felt::new), - Self::U16(b) => Some(Felt::new(b as u64)), - Self::I16(b) => u64::try_from(b).ok().map(Felt::new), - Self::U32(b) => Some(Felt::new(b as u64)), - Self::I32(b) => u64::try_from(b).ok().map(Felt::new), - Self::U64(b) => Some(Felt::new(b)), - Self::I64(b) => u64::try_from(b).ok().map(Felt::new), - Self::Felt(i) => Some(i), - Self::I128(b) => u64::try_from(b).ok().map(Felt::new), - Self::F64(f) => FloatToInt::::to_int(f).ok(), - } - } - - /// Attempts to convert this value to u64 - pub fn as_u64(self) -> Option { - match self { - Self::I1(b) => Some(b as u64), - Self::U8(i) => Some(i as u64), - Self::I8(i) if i >= 0 => Some(i as u64), - Self::I8(_) => None, - Self::U16(i) => Some(i as u64), - Self::I16(i) if i >= 0 => Some(i as u16 as u64), - Self::I16(_) => None, - Self::U32(i) => Some(i as u64), - Self::I32(i) if i >= 0 => Some(i as u32 as u64), - Self::I32(_) => None, - Self::U64(i) => Some(i), - Self::I64(i) if i >= 0 => Some(i as u64), - Self::I64(_) => None, - Self::Felt(i) => Some(i.as_int()), - Self::I128(i) if i >= 0 => (i).try_into().ok(), - Self::I128(_) => None, - Self::F64(f) => FloatToInt::::to_int(f).ok(), - } - } - - /// Attempts to convert this value to i64 - pub fn as_i64(self) -> Option { - match self { - Self::I1(b) => Some(b as i64), - Self::U8(i) => Some(i as i64), - Self::I8(i) => Some(i as i64), - Self::U16(i) => Some(i as i64), - Self::I16(i) => Some(i as i64), - Self::U32(i) => Some(i as i64), - Self::I32(i) => Some(i as i64), - Self::U64(i) => (i).try_into().ok(), - Self::I64(i) => Some(i), - Self::Felt(i) => i.as_int().try_into().ok(), - Self::I128(i) => (i).try_into().ok(), - Self::F64(f) => FloatToInt::::to_int(f).ok(), - } - } - - /// Attempts to convert this value to i128 - pub fn as_i128(self) -> Option { - match self { - Self::I1(b) => Some(b as i128), - Self::U8(i) => Some(i as i128), - Self::I8(i) => Some(i as i128), - Self::U16(i) => Some(i as i128), - Self::I16(i) => Some(i as i128), - Self::U32(i) => Some(i as i128), - Self::I32(i) => Some(i as i128), - Self::U64(i) => Some(i as i128), - Self::I64(i) => Some(i as i128), - Self::Felt(i) => Some(i.as_int() as i128), - Self::I128(i) => Some(i), - Self::F64(f) => FloatToInt::::to_int(f).ok(), - } - } -} -impl fmt::Display for Immediate { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::I1(i) => write!(f, "{}", i), - Self::U8(i) => write!(f, "{}", i), - Self::I8(i) => write!(f, "{}", i), - Self::U16(i) => write!(f, "{}", i), - Self::I16(i) => write!(f, "{}", i), - Self::U32(i) => write!(f, "{}", i), - Self::I32(i) => write!(f, "{}", i), - Self::U64(i) => write!(f, "{}", i), - Self::I64(i) => write!(f, "{}", i), - Self::I128(i) => write!(f, "{}", i), - Self::F64(n) => write!(f, "{}", n), - Self::Felt(i) => write!(f, "{}", i), - } - } -} -impl Hash for Immediate { - fn hash(&self, state: &mut H) { - let d = std::mem::discriminant(self); - d.hash(state); - match self { - Self::I1(i) => i.hash(state), - Self::U8(i) => i.hash(state), - Self::I8(i) => i.hash(state), - Self::U16(i) => i.hash(state), - Self::I16(i) => i.hash(state), - Self::U32(i) => i.hash(state), - Self::I32(i) => i.hash(state), - Self::U64(i) => i.hash(state), - Self::I64(i) => i.hash(state), - Self::I128(i) => i.hash(state), - Self::F64(f) => { - let bytes = f.to_be_bytes(); - bytes.hash(state) - } - Self::Felt(i) => i.as_int().hash(state), - } - } -} -impl Eq for Immediate {} -impl PartialEq for Immediate { - fn eq(&self, other: &Self) -> bool { - match (*self, *other) { - (Self::I8(x), Self::I8(y)) => x == y, - (Self::U16(x), Self::U16(y)) => x == y, - (Self::I16(x), Self::I16(y)) => x == y, - (Self::U32(x), Self::U32(y)) => x == y, - (Self::I32(x), Self::I32(y)) => x == y, - (Self::U64(x), Self::U64(y)) => x == y, - (Self::I64(x), Self::I64(y)) => x == y, - (Self::I128(x), Self::I128(y)) => x == y, - (Self::F64(x), Self::F64(y)) => x == y, - (Self::Felt(x), Self::Felt(y)) => x == y, - _ => false, - } - } -} -impl PartialEq for Immediate { - fn eq(&self, other: &isize) -> bool { - let y = *other; - match *self { - Self::I1(x) => x == (y == 1), - Self::U8(_) if y < 0 => false, - Self::U8(x) => x as isize == y, - Self::I8(x) => x as isize == y, - Self::U16(_) if y < 0 => false, - Self::U16(x) => x as isize == y, - Self::I16(x) => x as isize == y, - Self::U32(_) if y < 0 => false, - Self::U32(x) => x as isize == y, - Self::I32(x) => x as isize == y, - Self::U64(_) if y < 0 => false, - Self::U64(x) => x == y as i64 as u64, - Self::I64(x) => x == y as i64, - Self::I128(x) => x == y as i128, - Self::F64(_) => false, - Self::Felt(_) if y < 0 => false, - Self::Felt(x) => x.as_int() == y as i64 as u64, - } - } -} -impl PartialOrd for Immediate { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for Immediate { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - use std::cmp::Ordering; - - match (self, other) { - // Floats require special treatment - (Self::F64(x), Self::F64(y)) => x.total_cmp(y), - // Here we're attempting to compare against any integer immediate, - // so we must attempt to convert the float to the largest possible - // integer representation, i128, and then promote the integer immediate - // to i128 for comparison - // - // If the float is not an integer value, truncate it and compare, then - // adjust the result to account for the truncation - (Self::F64(x), y) => { - let y = y - .as_i128() - .expect("expected rhs to be an integer capable of fitting in an i128"); - if let Ok(x) = FloatToInt::::to_int(*x) { - x.cmp(&y) - } else { - let is_positive = x.is_sign_positive(); - if let Ok(x) = FloatToInt::::to_int((*x).trunc()) { - // Edge case for equality: the float must be bigger due to truncation - match x.cmp(&y) { - Ordering::Equal if is_positive => Ordering::Greater, - Ordering::Equal => Ordering::Less, - o => o, - } - } else { - // The float is larger than i128 can represent, the sign tells us in what direction - if is_positive { - Ordering::Greater - } else { - Ordering::Less - } - } - } - } - (x, y @ Self::F64(_)) => y.cmp(x).reverse(), - // i128 immediates require separate treatment - (Self::I128(x), Self::I128(y)) => x.cmp(y), - // We're only comparing against values here which are u64, i64, or smaller than 64-bits - (Self::I128(x), y) => { - let y = y - .as_i128() - .expect("expected rhs to be an integer smaller than i128"); - x.cmp(&y) - } - (x, Self::I128(y)) => { - let x = x - .as_i128() - .expect("expected rhs to be an integer smaller than i128"); - x.cmp(y) - } - // u64 immediates may not fit in an i64 - (Self::U64(x), Self::U64(y)) => x.cmp(y), - // We're only comparing against values here which are i64, or smaller than 64-bits - (Self::U64(x), y) => { - let y = y - .as_i64() - .expect("expected rhs to be an integer capable of fitting in an i64") - as u64; - x.cmp(&y) - } - (x, Self::U64(y)) => { - let x = x - .as_i64() - .expect("expected rhs to be an integer capable of fitting in an i64") - as u64; - x.cmp(y) - } - // All immediates at this point are i64 or smaller - (x, y) => { - let x = x - .as_i64() - .expect("expected rhs to be an integer capable of fitting in an i64"); - let y = y - .as_i64() - .expect("expected rhs to be an integer capable of fitting in an i64"); - x.cmp(&y) - } - } - } -} -impl From for Type { - #[inline] - fn from(imm: Immediate) -> Self { - imm.ty() - } -} -impl From<&Immediate> for Type { - #[inline(always)] - fn from(imm: &Immediate) -> Self { - imm.ty() - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: bool) -> Self { - Self::I1(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: i8) -> Self { - Self::I8(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: u8) -> Self { - Self::U8(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: i16) -> Self { - Self::I16(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: u16) -> Self { - Self::U16(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: i32) -> Self { - Self::I32(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: u32) -> Self { - Self::U32(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: i64) -> Self { - Self::I64(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: u64) -> Self { - Self::U64(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: i128) -> Self { - Self::I128(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: f64) -> Self { - Self::F64(value) - } -} -impl From for Immediate { - #[inline(always)] - fn from(value: char) -> Self { - Self::I32(value as u32 as i32) - } -} - -trait FloatToInt: Sized { - const ZERO: T; - - fn upper_bound() -> Self; - fn lower_bound() -> Self; - fn to_int(self) -> Result; - unsafe fn to_int_unchecked(self) -> T; -} -impl FloatToInt for f64 { - const ZERO: i8 = 0; - - fn upper_bound() -> Self { - f64::from(i8::MAX) + 1.0 - } - fn lower_bound() -> Self { - f64::from(i8::MIN) - 1.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> i8 { - f64::to_int_unchecked(self) - } -} -impl FloatToInt for f64 { - const ZERO: u8 = 0; - - fn upper_bound() -> Self { - f64::from(u8::MAX) + 1.0 - } - fn lower_bound() -> Self { - 0.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> u8 { - f64::to_int_unchecked(self) - } -} -impl FloatToInt for f64 { - const ZERO: i16 = 0; - - fn upper_bound() -> Self { - f64::from(i16::MAX) + 1.0 - } - fn lower_bound() -> Self { - f64::from(i16::MIN) - 1.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> i16 { - f64::to_int_unchecked(self) - } -} -impl FloatToInt for f64 { - const ZERO: u16 = 0; - - fn upper_bound() -> Self { - f64::from(u16::MAX) + 1.0 - } - fn lower_bound() -> Self { - 0.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> u16 { - f64::to_int_unchecked(self) - } -} -impl FloatToInt for f64 { - const ZERO: i32 = 0; - - fn upper_bound() -> Self { - f64::from(i32::MAX) + 1.0 - } - fn lower_bound() -> Self { - f64::from(i32::MIN) - 1.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> i32 { - f64::to_int_unchecked(self) - } -} -impl FloatToInt for f64 { - const ZERO: u32 = 0; - - fn upper_bound() -> Self { - f64::from(u32::MAX) + 1.0 - } - fn lower_bound() -> Self { - 0.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> u32 { - f64::to_int_unchecked(self) - } -} -impl FloatToInt for f64 { - const ZERO: i64 = 0; - - fn upper_bound() -> Self { - 63.0f64.exp2() - } - fn lower_bound() -> Self { - (63.0f64.exp2() * -1.0) - 1.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> i64 { - f64::to_int_unchecked(self) - } -} -impl FloatToInt for f64 { - const ZERO: u64 = 0; - - fn upper_bound() -> Self { - 64.0f64.exp2() - } - fn lower_bound() -> Self { - 0.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> u64 { - f64::to_int_unchecked(self) - } -} -impl FloatToInt for f64 { - const ZERO: Felt = Felt::ZERO; - - fn upper_bound() -> Self { - 64.0f64.exp2() - 32.0f64.exp2() + 1.0 - } - fn lower_bound() -> Self { - 0.0 - } - fn to_int(self) -> Result { - float_to_int(self).map(Felt::new) - } - unsafe fn to_int_unchecked(self) -> Felt { - Felt::new(f64::to_int_unchecked::(self)) - } -} -impl FloatToInt for f64 { - const ZERO: i128 = 0; - - fn upper_bound() -> Self { - f64::from(i128::BITS - 1).exp2() - } - fn lower_bound() -> Self { - (f64::from(i128::BITS - 1) * -1.0).exp2() - 1.0 - } - fn to_int(self) -> Result { - float_to_int(self) - } - unsafe fn to_int_unchecked(self) -> i128 { - f64::to_int_unchecked(self) - } -} - -fn float_to_int(f: f64) -> Result -where - I: Copy, - f64: FloatToInt, -{ - use std::num::FpCategory; - match f.classify() { - FpCategory::Nan | FpCategory::Infinite | FpCategory::Subnormal => Err(()), - FpCategory::Zero => Ok(>::ZERO), - FpCategory::Normal => { - if f == f.trunc() - && f > >::lower_bound() - && f < >::upper_bound() - { - // SAFETY: We know that x must be integral, and within the bounds of its type - Ok(unsafe { >::to_int_unchecked(f) }) - } else { - Err(()) - } - } - } -} diff --git a/hir/src/insert.rs b/hir/src/insert.rs deleted file mode 100644 index 258a4eae1..000000000 --- a/hir/src/insert.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::{Block, Function, ProgramPoint}; - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum Insert { - Before, - After, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct InsertionPoint { - pub at: ProgramPoint, - pub action: Insert, -} -impl InsertionPoint { - #[inline] - pub const fn new(at: ProgramPoint, action: Insert) -> Self { - Self { at, action } - } - - #[inline] - pub const fn before(at: ProgramPoint) -> Self { - Self { - at, - action: Insert::Before, - } - } - - #[inline] - pub const fn after(at: ProgramPoint) -> Self { - Self { - at, - action: Insert::After, - } - } - - pub fn block(&self, function: &Function) -> Block { - match self.at { - ProgramPoint::Block(block) => block, - ProgramPoint::Inst(inst) => function - .dfg - .inst_block(inst) - .expect("cannot insert relative to detached instruction"), - } - } -} diff --git a/hir/src/instruction.rs b/hir/src/instruction.rs deleted file mode 100644 index 99f33a230..000000000 --- a/hir/src/instruction.rs +++ /dev/null @@ -1,1039 +0,0 @@ -use std::{ - convert::{AsMut, AsRef}, - fmt, - ops::{Deref, DerefMut}, -}; - -use cranelift_entity::entity_impl; -use intrusive_collections::{intrusive_adapter, LinkedListLink, UnsafeRef}; -use smallvec::SmallVec; - -use miden_diagnostics::{Span, Spanned}; - -use super::*; - -/// A handle to a single instruction -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Inst(u32); -entity_impl!(Inst, "inst"); - -/// Represents the data associated with an `Inst`. -/// -/// Specifically, this represents a leaf node in the control flow graph of -/// a function, i.e. it links a specific instruction in to the sequence of -/// instructions belonging to a specific block. -#[derive(Spanned)] -pub struct InstNode { - pub link: LinkedListLink, - pub key: Inst, - pub block: Block, - #[span] - pub data: Span, -} -impl fmt::Debug for InstNode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", &self.data) - } -} -impl InstNode { - pub fn new(key: Inst, block: Block, data: Span) -> Self { - Self { - link: LinkedListLink::default(), - key, - block, - data, - } - } - - pub fn deep_clone(&self, value_lists: &mut ValueListPool) -> Self { - let span = self.data.span(); - Self { - link: LinkedListLink::default(), - key: self.key, - block: self.block, - data: Span::new(span, self.data.deep_clone(value_lists)), - } - } - - pub fn replace(&mut self, data: Span) { - self.data = data; - } -} -impl Deref for InstNode { - type Target = Instruction; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.data - } -} -impl DerefMut for InstNode { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data - } -} -impl AsRef for InstNode { - #[inline] - fn as_ref(&self) -> &Instruction { - &self.data - } -} -impl AsMut for InstNode { - #[inline] - fn as_mut(&mut self) -> &mut Instruction { - &mut self.data - } -} - -intrusive_adapter!(pub InstAdapter = UnsafeRef: InstNode { link: LinkedListLink }); - -/// A type alias for `LinkedList` -pub type InstructionList = intrusive_collections::LinkedList; - -/// Represents the type of instruction associated with a particular opcode -#[derive(Debug)] -pub enum Instruction { - GlobalValue(GlobalValueOp), - BinaryOp(BinaryOp), - BinaryOpImm(BinaryOpImm), - UnaryOp(UnaryOp), - UnaryOpImm(UnaryOpImm), - Call(Call), - Br(Br), - CondBr(CondBr), - Switch(Switch), - Ret(Ret), - RetImm(RetImm), - Load(LoadOp), - PrimOp(PrimOp), - PrimOpImm(PrimOpImm), - Test(Test), - InlineAsm(InlineAsm), -} -impl Instruction { - pub fn deep_clone(&self, value_lists: &mut ValueListPool) -> Self { - match self { - Self::GlobalValue(gv) => Self::GlobalValue(gv.clone()), - Self::BinaryOp(op) => Self::BinaryOp(op.clone()), - Self::BinaryOpImm(op) => Self::BinaryOpImm(op.clone()), - Self::UnaryOp(op) => Self::UnaryOp(op.clone()), - Self::UnaryOpImm(op) => Self::UnaryOpImm(op.clone()), - Self::Call(call) => Self::Call(Call { - args: call.args.deep_clone(value_lists), - ..call.clone() - }), - Self::Br(br) => Self::Br(Br { - args: br.args.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)), - ..br.clone() - }), - Self::Switch(op) => Self::Switch(op.clone()), - Self::Ret(op) => Self::Ret(Ret { - args: op.args.deep_clone(value_lists), - ..op.clone() - }), - Self::RetImm(op) => Self::RetImm(op.clone()), - Self::Load(op) => Self::Load(op.clone()), - Self::PrimOp(op) => Self::PrimOp(PrimOp { - args: op.args.deep_clone(value_lists), - ..op.clone() - }), - Self::PrimOpImm(op) => Self::PrimOpImm(PrimOpImm { - args: op.args.deep_clone(value_lists), - ..op.clone() - }), - Self::Test(op) => Self::Test(op.clone()), - Self::InlineAsm(op) => Self::InlineAsm(InlineAsm { - args: op.args.deep_clone(value_lists), - ..op.clone() - }), - } - } - - pub fn opcode(&self) -> Opcode { - match self { - Self::GlobalValue(GlobalValueOp { ref op, .. }) - | Self::BinaryOp(BinaryOp { ref op, .. }) - | Self::BinaryOpImm(BinaryOpImm { ref op, .. }) - | Self::UnaryOp(UnaryOp { ref op, .. }) - | Self::UnaryOpImm(UnaryOpImm { ref op, .. }) - | Self::Call(Call { ref op, .. }) - | Self::Br(Br { ref op, .. }) - | Self::CondBr(CondBr { ref op, .. }) - | Self::Switch(Switch { ref op, .. }) - | Self::Ret(Ret { ref op, .. }) - | Self::RetImm(RetImm { ref op, .. }) - | Self::Load(LoadOp { ref op, .. }) - | Self::PrimOp(PrimOp { ref op, .. }) - | Self::PrimOpImm(PrimOpImm { ref op, .. }) - | Self::Test(Test { ref op, .. }) - | Self::InlineAsm(InlineAsm { ref op, .. }) => *op, - } - } - - /// Returns true if this instruction has side effects, or may have side effects - /// - /// Side effects are defined as control flow, writing memory, trapping execution, - /// I/O, etc. - /// - #[inline] - pub fn has_side_effects(&self) -> bool { - self.opcode().has_side_effects() - } - - /// Returns true if this instruction is a binary operator requiring two operands - /// - /// NOTE: Binary operators with immediate operands are not considered binary for - /// this purpose, as they only require a single operand to be provided to the - /// instruction, the immediate being the other one provided by the instruction - /// itself. - pub fn is_binary(&self) -> bool { - matches!(self, Self::BinaryOp(_)) - } - - /// Returns true if this instruction is a binary operator whose operands may - /// appear in any order. - #[inline] - pub fn is_commutative(&self) -> bool { - self.opcode().is_commutative() - } - - /// Get the [Overflow] flag for this instruction, if applicable - pub fn overflow(&self) -> Option { - match self { - Self::BinaryOp(BinaryOp { overflow, .. }) - | Self::BinaryOpImm(BinaryOpImm { overflow, .. }) - | Self::UnaryOp(UnaryOp { overflow, .. }) - | Self::UnaryOpImm(UnaryOpImm { overflow, .. }) => *overflow, - _ => None, - } - } - - pub fn arguments<'a>(&'a self, pool: &'a ValueListPool) -> &[Value] { - match self { - Self::BinaryOp(BinaryOp { ref args, .. }) => args.as_slice(), - Self::BinaryOpImm(BinaryOpImm { ref arg, .. }) => core::slice::from_ref(arg), - Self::UnaryOp(UnaryOp { ref arg, .. }) => core::slice::from_ref(arg), - Self::Call(Call { ref args, .. }) => args.as_slice(pool), - Self::CondBr(CondBr { ref cond, .. }) => core::slice::from_ref(cond), - Self::Switch(Switch { ref arg, .. }) => core::slice::from_ref(arg), - Self::Ret(Ret { ref args, .. }) => args.as_slice(pool), - Self::Load(LoadOp { ref addr, .. }) => core::slice::from_ref(addr), - Self::PrimOp(PrimOp { ref args, .. }) => args.as_slice(pool), - Self::PrimOpImm(PrimOpImm { ref args, .. }) => args.as_slice(pool), - Self::Test(Test { ref arg, .. }) => core::slice::from_ref(arg), - Self::InlineAsm(InlineAsm { ref args, .. }) => args.as_slice(pool), - Self::GlobalValue(_) | Self::UnaryOpImm(_) | Self::Br(_) | Self::RetImm(_) => &[], - } - } - - pub fn arguments_mut<'a>(&'a mut self, pool: &'a mut ValueListPool) -> &mut [Value] { - match self { - Self::BinaryOp(BinaryOp { ref mut args, .. }) => args.as_mut_slice(), - Self::BinaryOpImm(BinaryOpImm { ref mut arg, .. }) => core::slice::from_mut(arg), - Self::UnaryOp(UnaryOp { ref mut arg, .. }) => core::slice::from_mut(arg), - Self::Call(Call { ref mut args, .. }) => args.as_mut_slice(pool), - Self::CondBr(CondBr { ref mut cond, .. }) => core::slice::from_mut(cond), - Self::Switch(Switch { ref mut arg, .. }) => core::slice::from_mut(arg), - Self::Ret(Ret { ref mut args, .. }) => args.as_mut_slice(pool), - Self::Load(LoadOp { ref mut addr, .. }) => core::slice::from_mut(addr), - Self::PrimOp(PrimOp { ref mut args, .. }) => args.as_mut_slice(pool), - Self::PrimOpImm(PrimOpImm { ref mut args, .. }) => args.as_mut_slice(pool), - Self::Test(Test { ref mut arg, .. }) => core::slice::from_mut(arg), - Self::InlineAsm(InlineAsm { ref mut args, .. }) => args.as_mut_slice(pool), - Self::GlobalValue(_) | Self::UnaryOpImm(_) | Self::Br(_) | Self::RetImm(_) => &mut [], - } - } - - 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::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)), - ]), - Self::Switch(Switch { - ref arms, - ref default, - .. - }) => { - let mut targets = arms - .iter() - .map(|(_, b)| JumpTable::new(*b, &[])) - .collect::>(); - targets.push(JumpTable::new(*default, &[])); - BranchInfo::MultiDest(targets) - } - _ => BranchInfo::NotABranch, - } - } - - pub fn analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a> { - match self { - Self::Call(ref c) => CallInfo::Direct(c.callee, c.args.as_slice(pool)), - _ => CallInfo::NotACall, - } - } -} - -#[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 } - } -} - -pub enum CallInfo<'a> { - NotACall, - Direct(FunctionIdent, &'a [Value]), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum Opcode { - /// Asserts the given value is 1 - Assert, - /// Asserts the given value is 0 - Assertz, - /// Asserts the two given values are equal - AssertEq, - /// Represents an immediate boolean value (1-bit integer) - ImmI1, - /// Represents an immediate unsigned 8-bit integer value - ImmU8, - /// Represents an immediate signed 8-bit integer value - ImmI8, - /// Represents an immediate unsigned 16-bit integer value - ImmU16, - /// Represents an immediate signed 16-bit integer value - ImmI16, - /// Represents an immediate unsigned 32-bit integer value - ImmU32, - /// Represents an immediate signed 32-bit integer value - ImmI32, - /// Represents an immediate unsigned 64-bit integer value - ImmU64, - /// Represents an immediate signed 64-bit integer value - ImmI64, - /// Represents an immediate field element - ImmFelt, - /// Represents an immediate 64-bit floating-point value - ImmF64, - /// Allocates a new "null" value in a temporary memory slot, where null is defined by - /// the semantics of the type. The result of this instruction is always a pointer to - /// the allocated type. - /// - /// For integral types, the null value is always zero. - /// - /// For pointer types, the null value is equal to the address of the start of the linear - /// memory range, i.e. address `0x0`. - /// - /// For structs and arrays, the null value is a value equal in size (in bytes) to the size - /// of the type, but whose contents are undefined, i.e. you cannot assume that the binary - /// representation of the value is zeroed. - Alloca, - /// Like the WebAssembly `memory.grow` instruction, this allocates a given number of pages from the - /// global heap, and returns the previous size of the heap, in pages. Each page is 64kb by default. - /// - /// For the time being, this instruction is emulated using a heap pointer global which tracks - /// the "end" of the available heap. Nothing actually prevents one from accessing memory past - /// that point (assuming it is within the 32-bit address range), however this allows us to - /// support code compiled for the `wasm32-unknown-unknown` target cleanly. - MemGrow, - /// This instruction is used to represent a global value in the IR - /// - /// See [GlobalValueOp] and [GlobalValueData] for details on what types of values are represented - /// behind this opcode. - GlobalValue, - /// Loads a value from a pointer to memory - Load, - /// Stores a value to a pointer to memory - Store, - /// Copies `n` values of a given type from a source pointer to a destination pointer - MemCpy, - /// Casts a pointer value to an integral type - PtrToInt, - /// Casts an integral type to a pointer value - IntToPtr, - /// Casts from a field element type to an integral type - /// - /// It is not valid to perform a cast on any value other than a field element, see - /// `Trunc`, `Zext`, and `Sext` for casts between machine integer types. - Cast, - /// Truncates a larger integral type to a smaller integral type, e.g. i64 -> i32 - Trunc, - /// Zero-extends a smaller unsigned integral type to a larger unsigned integral type, e.g. u32 -> u64 - Zext, - /// Sign-extends a smaller signed integral type to a larger signed integral type, e.g. i32 -> i64 - Sext, - /// Returns true if argument fits in the given integral type, e.g. u32, otherwise false - Test, - /// Selects between two values given a conditional - Select, - Add, - Sub, - Mul, - Div, - Mod, - DivMod, - Neg, - Inv, - Incr, - Pow2, - Exp, - Not, - Bnot, - And, - Band, - Or, - Bor, - Xor, - Bxor, - Shl, - Shr, - Rotl, - Rotr, - Popcnt, - Eq, - Neq, - Gt, - Gte, - Lt, - Lte, - IsOdd, - Min, - Max, - Call, - Syscall, - Br, - CondBr, - Switch, - Ret, - Unreachable, - InlineAsm, -} -impl Opcode { - pub fn is_terminator(&self) -> bool { - matches!( - self, - Self::Br | Self::CondBr | Self::Switch | Self::Ret | Self::Unreachable - ) - } - - pub fn is_branch(&self) -> bool { - matches!(self, Self::Br | Self::CondBr | Self::Switch) - } - - pub fn is_call(&self) -> bool { - matches!(self, Self::Call | Self::Syscall) - } - - pub fn is_commutative(&self) -> bool { - matches!( - self, - Self::Add - | Self::Mul - | Self::Min - | Self::Max - | Self::Eq - | Self::Neq - | Self::And - | Self::Band - | Self::Or - | Self::Bor - | Self::Xor - | Self::Bxor - ) - } - - pub fn has_side_effects(&self) -> bool { - match self { - // These opcodes are all effectful - Self::Assert - | Self::Assertz - | Self::AssertEq - | Self::Store - | Self::Alloca - | Self::MemCpy - | Self::MemGrow - | Self::Call - | Self::Syscall - | Self::Br - | Self::CondBr - | Self::Switch - | Self::Ret - | Self::Unreachable - | Self::InlineAsm => true, - // These opcodes are not - Self::ImmI1 - | Self::ImmU8 - | Self::ImmI8 - | Self::ImmU16 - | Self::ImmI16 - | Self::ImmU32 - | Self::ImmI32 - | Self::ImmU64 - | Self::ImmI64 - | Self::ImmFelt - | Self::ImmF64 - | Self::GlobalValue - | Self::Load - | Self::PtrToInt - | Self::IntToPtr - | Self::Cast - | Self::Trunc - | Self::Zext - | Self::Sext - | Self::Test - | Self::Select - | Self::Add - | Self::Sub - | Self::Mul - | Self::Div - | Self::Mod - | Self::DivMod - | Self::Neg - | Self::Inv - | Self::Incr - | Self::Pow2 - | Self::Exp - | Self::Not - | Self::Bnot - | Self::And - | Self::Band - | Self::Or - | Self::Bor - | Self::Xor - | Self::Bxor - | Self::Shl - | Self::Shr - | Self::Rotl - | Self::Rotr - | Self::Popcnt - | Self::Eq - | Self::Neq - | Self::Gt - | Self::Gte - | Self::Lt - | Self::Lte - | Self::IsOdd - | Self::Min - | Self::Max => false, - } - } - - pub fn num_fixed_args(&self) -> usize { - match self { - Self::Assert | Self::Assertz => 1, - Self::AssertEq => 2, - // Immediates/constants have none - Self::ImmI1 - | Self::ImmU8 - | Self::ImmI8 - | Self::ImmU16 - | Self::ImmI16 - | Self::ImmU32 - | Self::ImmI32 - | Self::ImmU64 - | Self::ImmI64 - | Self::ImmFelt - | Self::ImmF64 => 0, - // Binary ops always have two - Self::Store - | Self::Add - | Self::Sub - | Self::Mul - | Self::Div - | Self::Mod - | Self::DivMod - | Self::Exp - | Self::And - | Self::Band - | Self::Or - | Self::Bor - | Self::Xor - | Self::Bxor - | Self::Shl - | Self::Shr - | Self::Rotl - | Self::Rotr - | Self::Eq - | Self::Neq - | Self::Gt - | Self::Gte - | Self::Lt - | Self::Lte - | Self::Min - | Self::Max => 2, - // Unary ops always have one - Self::MemGrow - | Self::Load - | Self::PtrToInt - | Self::IntToPtr - | Self::Cast - | Self::Trunc - | Self::Zext - | Self::Sext - | Self::Test - | Self::Neg - | Self::Inv - | Self::Incr - | Self::Pow2 - | Self::Popcnt - | Self::Not - | Self::Bnot - | Self::IsOdd => 1, - // Select requires condition, arg1, and arg2 - Self::Select => 3, - // MemCpy requires source, destination, and arity - Self::MemCpy => 3, - // Calls are entirely variable - Self::Call | Self::Syscall => 0, - // Unconditional branches have no fixed arguments - Self::Br => 0, - // Ifs have a single argument, the conditional - Self::CondBr => 1, - // Switches have a single argument, the input value - Self::Switch => 1, - // Returns require at least one argument - Self::Ret => 1, - // The following require no arguments - Self::GlobalValue | Self::Alloca | Self::Unreachable | Self::InlineAsm => 0, - } - } - - pub(super) fn results(&self, overflow: Option, ctrl_ty: Type) -> SmallVec<[Type; 1]> { - use smallvec::smallvec; - - match self { - // These ops have no results - Self::Assert - | Self::Assertz - | Self::AssertEq - | Self::Store - | Self::MemCpy - | Self::Br - | Self::CondBr - | Self::Switch - | Self::Ret - | Self::Unreachable => smallvec![], - // These ops have fixed result types - Self::Test - | Self::IsOdd - | Self::Not - | Self::And - | Self::Or - | Self::Xor - | Self::Eq - | Self::Neq - | Self::Gt - | Self::Gte - | Self::Lt - | Self::Lte => smallvec![Type::I1], - // For these ops, the controlling type variable determines the type for the op - Self::ImmI1 - | Self::ImmU8 - | Self::ImmI8 - | Self::ImmU16 - | Self::ImmI16 - | Self::ImmU32 - | Self::ImmI32 - | Self::ImmU64 - | Self::ImmI64 - | Self::ImmFelt - | Self::ImmF64 - | Self::GlobalValue - | Self::Alloca - | Self::PtrToInt - | Self::IntToPtr - | Self::Cast - | Self::Trunc - | Self::Zext - | Self::Sext - | Self::Select - | Self::Div - | Self::Min - | Self::Max - | Self::Neg - | Self::Inv - | Self::Pow2 - | Self::Popcnt - | Self::Mod - | Self::DivMod - | Self::Exp - | Self::Bnot - | Self::Band - | Self::Bor - | Self::Bxor - | Self::Rotl - | Self::Rotr - | Self::MemGrow => { - smallvec![ctrl_ty] - } - // These ops have overflowing variants which returns an additional result in that case - Self::Add | Self::Sub | Self::Mul | Self::Incr | Self::Shl | Self::Shr => { - match overflow { - Some(Overflow::Overflowing) => smallvec![Type::I1, ctrl_ty], - _ => smallvec![ctrl_ty], - } - } - // The result type of a load is derived from the pointee type - Self::Load => { - smallvec![ctrl_ty.pointee().expect("expected pointer type").clone()] - } - // Call results are handled separately - Self::Call | Self::Syscall | Self::InlineAsm => unreachable!(), - } - } -} -impl fmt::Display for Opcode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Assert => f.write_str("assert"), - Self::Assertz => f.write_str("assertz"), - Self::AssertEq => f.write_str("assert.eq"), - Self::ImmI1 => f.write_str("const.i1"), - Self::ImmU8 => f.write_str("const.u8"), - Self::ImmI8 => f.write_str("const.i8"), - Self::ImmU16 => f.write_str("const.u16"), - Self::ImmI16 => f.write_str("const.i16"), - Self::ImmU32 => f.write_str("const.u32"), - Self::ImmI32 => f.write_str("const.i32"), - Self::ImmU64 => f.write_str("const.u64"), - Self::ImmI64 => f.write_str("const.i64"), - Self::ImmFelt => f.write_str("const.felt"), - Self::ImmF64 => f.write_str("const.f64"), - Self::GlobalValue => f.write_str("global"), - Self::Alloca => f.write_str("alloca"), - Self::MemGrow => f.write_str("memory.grow"), - Self::Load => f.write_str("load"), - Self::Store => f.write_str("store"), - Self::MemCpy => f.write_str("memcpy"), - Self::PtrToInt => f.write_str("ptrtoint"), - Self::IntToPtr => f.write_str("inttoptr"), - Self::Cast => f.write_str("cast"), - Self::Trunc => f.write_str("trunc"), - Self::Zext => f.write_str("zext"), - Self::Sext => f.write_str("sext"), - Self::Br => f.write_str("br"), - Self::CondBr => f.write_str("condbr"), - Self::Switch => f.write_str("switch"), - Self::Call => f.write_str("call"), - Self::Syscall => f.write_str("syscall"), - Self::Ret => f.write_str("ret"), - Self::Test => f.write_str("test"), - Self::Select => f.write_str("select"), - Self::Add => f.write_str("add"), - Self::Sub => f.write_str("sub"), - Self::Mul => f.write_str("mul"), - Self::Div => f.write_str("div"), - Self::Mod => f.write_str("mod"), - Self::DivMod => f.write_str("divmod"), - Self::Exp => f.write_str("exp"), - Self::Neg => f.write_str("neg"), - Self::Inv => f.write_str("inv"), - Self::Incr => f.write_str("incr"), - Self::Pow2 => f.write_str("pow2"), - Self::Not => f.write_str("not"), - Self::Bnot => f.write_str("bnot"), - Self::And => f.write_str("and"), - Self::Band => f.write_str("band"), - Self::Or => f.write_str("or"), - Self::Bor => f.write_str("bor"), - Self::Xor => f.write_str("xor"), - Self::Bxor => f.write_str("bxor"), - Self::Shl => f.write_str("shl"), - Self::Shr => f.write_str("shr"), - Self::Rotl => f.write_str("rotl"), - Self::Rotr => f.write_str("rotr"), - Self::Popcnt => f.write_str("popcnt"), - Self::Eq => f.write_str("eq"), - Self::Neq => f.write_str("neq"), - Self::Gt => f.write_str("gt"), - Self::Gte => f.write_str("gte"), - Self::Lt => f.write_str("lt"), - Self::Lte => f.write_str("lte"), - Self::IsOdd => f.write_str("is_odd"), - Self::Min => f.write_str("min"), - Self::Max => f.write_str("max"), - Self::Unreachable => f.write_str("unreachable"), - Self::InlineAsm => f.write_str("asm"), - } - } -} - -/// This enumeration represents the various ways in which arithmetic operations -/// can be configured to behave when either the operands or results over/underflow -/// the range of the integral type. -/// -/// Always check the documentation of the specific instruction involved to see if there -/// are any specific differences in how this enum is interpreted compared to the default -/// meaning of each variant. -#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] -pub enum Overflow { - /// Typically, this means the operation is performed using the equivalent field element operation, rather - /// than a dedicated operation for the given type. Because of this, the result of the operation may exceed - /// that of the integral type expected, but this will not be caught right away. - /// - /// It is the callers responsibility to ensure that resulting value is in range. - #[default] - Unchecked, - /// The operation will trap if the operands, or the result, is not valid for the range of the integral - /// type involved, e.g. u32. - Checked, - /// The operation will wrap around, depending on the range of the integral type. For example, - /// given a u32 value, this is done by applying `mod 2^32` to the result. - Wrapping, - /// The result of the operation will be computed as in [Wrapping], however in addition to the - /// result, this variant also pushes a value on the stack which represents whether or not the - /// operation over/underflowed; either 1 if over/underflow occurred, or 0 otherwise. - Overflowing, -} -impl Overflow { - /// Returns true if overflow is unchecked - pub fn is_unchecked(&self) -> bool { - matches!(self, Self::Unchecked) - } - - /// Returns true if overflow will cause a trap - pub fn is_checked(&self) -> bool { - matches!(self, Self::Checked) - } - - /// Returns true if overflow will add an extra boolean on top of the stack - pub fn is_overflowing(&self) -> bool { - matches!(self, Self::Overflowing) - } -} -impl fmt::Display for Overflow { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Unchecked => f.write_str("unchecked"), - Self::Checked => f.write_str("checked"), - Self::Wrapping => f.write_str("wrapping"), - Self::Overflowing => f.write_str("overflow"), - } - } -} - -#[derive(Debug, Clone)] -pub struct GlobalValueOp { - pub op: Opcode, - pub global: GlobalValue, -} - -#[derive(Debug, Clone)] -pub struct BinaryOp { - pub op: Opcode, - pub overflow: Option, - /// NOTE: These arguments are in stack order, i.e. `a + b` will appear here as `[b, a]` - pub args: [Value; 2], -} - -#[derive(Debug, Clone)] -pub struct BinaryOpImm { - pub op: Opcode, - pub overflow: Option, - pub arg: Value, - pub imm: Immediate, -} - -#[derive(Debug, Clone)] -pub struct UnaryOp { - pub op: Opcode, - pub overflow: Option, - pub arg: Value, -} - -#[derive(Debug, Clone)] -pub struct UnaryOpImm { - pub op: Opcode, - pub overflow: Option, - pub imm: Immediate, -} - -#[derive(Debug, Clone)] -pub struct Call { - pub op: Opcode, - pub callee: FunctionIdent, - /// NOTE: Call arguments are always in stack order, i.e. the top operand on - /// the stack is the first function argument - pub args: ValueList, -} - -/// Branch -#[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, -} - -/// Conditional Branch -#[derive(Debug, Clone)] -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), -} - -/// Multi-way Branch w/Selector -#[derive(Debug, Clone)] -pub struct Switch { - pub op: Opcode, - pub arg: Value, - pub arms: Vec<(u32, Block)>, - pub default: Block, -} - -/// Return -#[derive(Debug, Clone)] -pub struct Ret { - pub op: Opcode, - /// NOTE: Return arguments are always in stack order, i.e. `ret a, b` - /// will appear on the stack as `[a, b]` - pub args: ValueList, -} - -/// Return an immediate -#[derive(Debug, Clone)] -pub struct RetImm { - pub op: Opcode, - pub arg: Immediate, -} - -/// Test -#[derive(Debug, Clone)] -pub struct Test { - pub op: Opcode, - pub arg: Value, - pub ty: Type, -} - -/// Load a value of type `ty` from `addr` -#[derive(Debug, Clone)] -pub struct LoadOp { - pub op: Opcode, - pub addr: Value, - pub ty: Type, -} - -/// A primop/intrinsic that takes a variable number of arguments -#[derive(Debug, Clone)] -pub struct PrimOp { - pub op: Opcode, - /// NOTE: Primops should be defined such that their arguments are in stack order - /// when they correspond to a MASM instruction, i.e. `assert_eq a, b` should appear `[b, a]` - pub args: ValueList, -} - -/// A primop that takes an immediate for its first argument, followed by a variable number of -/// arguments -#[derive(Debug, Clone)] -pub struct PrimOpImm { - pub op: Opcode, - pub imm: Immediate, - /// NOTE: Primops should be defined such that their arguments are in stack order - /// when they correspond to a MASM instruction, i.e. `assert_eq a, b` should appear `[b, a]`, - /// not counting the immediate argument - pub args: ValueList, -} - -#[doc(hidden)] -pub struct InstructionWithValueListPool<'a> { - pub inst: &'a Instruction, - pub value_lists: &'a ValueListPool, -} -impl<'a> PartialEq for InstructionWithValueListPool<'a> { - fn eq(&self, other: &Self) -> bool { - if core::mem::discriminant(self.inst) != core::mem::discriminant(other.inst) { - return false; - } - - if self.inst.opcode() != other.inst.opcode() { - return false; - } - - match (self.inst, other.inst) { - (Instruction::GlobalValue(l), Instruction::GlobalValue(r)) => l.global == r.global, - (Instruction::BinaryOp(l), Instruction::BinaryOp(r)) => { - l.overflow == r.overflow && l.args == r.args - } - (Instruction::BinaryOpImm(l), Instruction::BinaryOpImm(r)) => { - l.arg == r.arg && l.imm == r.imm && l.overflow == r.overflow - } - (Instruction::UnaryOp(l), Instruction::UnaryOp(r)) => { - l.arg == r.arg && l.overflow == r.overflow - } - (Instruction::UnaryOpImm(l), Instruction::UnaryOpImm(r)) => { - l.imm == r.imm && l.overflow == r.overflow - } - (Instruction::Call(l), Instruction::Call(r)) => { - l.callee == r.callee - && 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) - } - (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) - } - (Instruction::Switch(l), Instruction::Switch(r)) => { - l.arg == r.arg && l.default == r.default && l.arms == r.arms - } - (Instruction::Ret(l), Instruction::Ret(r)) => { - l.args.as_slice(self.value_lists) == r.args.as_slice(other.value_lists) - } - (Instruction::RetImm(l), Instruction::RetImm(r)) => l.arg == r.arg, - (Instruction::Load(l), Instruction::Load(r)) => l.addr == r.addr && l.ty == r.ty, - (Instruction::PrimOp(l), Instruction::PrimOp(r)) => { - l.args.as_slice(self.value_lists) == r.args.as_slice(other.value_lists) - } - (Instruction::PrimOpImm(l), Instruction::PrimOpImm(r)) => { - l.imm == r.imm - && l.args.as_slice(self.value_lists) == r.args.as_slice(other.value_lists) - } - (Instruction::Test(l), Instruction::Test(r)) => l.arg == r.arg && l.ty == r.ty, - (Instruction::InlineAsm(l), Instruction::InlineAsm(r)) => { - l.args.as_slice(self.value_lists) == r.args.as_slice(other.value_lists) - && l.results == r.results - && l.body == r.body - && l.blocks == r.blocks - } - (_, _) => unreachable!(), - } - } -} diff --git a/hir/src/layout.rs b/hir/src/layout.rs deleted file mode 100644 index a881bf9d2..000000000 --- a/hir/src/layout.rs +++ /dev/null @@ -1,484 +0,0 @@ -use std::{ - ops::{Index, IndexMut}, - ptr::NonNull, -}; - -use cranelift_entity::EntityRef; -use intrusive_collections::linked_list::{Cursor, CursorMut, LinkedList}; -use intrusive_collections::{intrusive_adapter, LinkedListLink, UnsafeRef}; -use typed_arena::Arena; - -/// This struct holds the data for each node in an ArenaMap/OrderedArenaMap -pub struct LayoutNode { - pub link: LinkedListLink, - key: K, - value: V, -} -impl Clone for LayoutNode { - fn clone(&self) -> Self { - Self { - link: LinkedListLink::new(), - key: self.key, - value: self.value.clone(), - } - } -} -impl LayoutNode { - pub fn new(key: K, value: V) -> Self { - Self { - link: LinkedListLink::default(), - key, - value, - } - } - - #[inline(always)] - pub fn key(&self) -> K { - self.key - } - - #[inline(always)] - pub fn value(&self) -> &V { - &self.value - } - - #[inline(always)] - pub fn value_mut(&mut self) -> &mut V { - &mut self.value - } -} - -intrusive_adapter!(pub LayoutAdapter = UnsafeRef>: LayoutNode { link: LinkedListLink } where K: EntityRef); - -/// ArenaMap provides similar functionality to other kinds of maps: -/// -/// # Pros -/// -/// * Once allocated, values stored in the map have a stable location, this can be useful for when you -/// expect to store elements of the map in an intrusive collection. -/// * Keys can be more efficiently sized, i.e. rather than pointers/usize keys, you can choose arbitrarily -/// small bitwidths, as long as there is sufficient keyspace for your use case. -/// * Attempt to keep data in the map as contiguous in memory as possible. This is again useful for when -/// the data is also linked into an intrusive collection, like a linked list, where traversing the list -/// will end up visiting many of the nodes in the map. If each node was its own Box, this would cause -/// thrashing of the cache - ArenaMap sidesteps this by allocating values in chunks of memory that are -/// friendlier to the cache. -/// -/// # Cons -/// -/// * Memory allocated for data stored in the map is not released until the map is dropped. This is -/// a tradeoff made to ensure that the data has a stable location in memory, but the flip side of that -/// is increased memory usage for maps that stick around for a long time. In our case, these maps are -/// relatively short-lived, so it isn't a problem in practice. -/// * It doesn't provide as rich of an API as HashMap and friends -pub struct ArenaMap { - keys: Vec>>, - arena: Arena, - _marker: core::marker::PhantomData, -} -impl Drop for ArenaMap { - fn drop(&mut self) { - self.keys.clear() - } -} -impl Clone for ArenaMap { - fn clone(&self) -> Self { - let mut cloned = Self::new(); - for opt in self.keys.iter() { - match opt { - None => cloned.keys.push(None), - Some(nn) => { - let value = unsafe { nn.as_ref() }; - cloned.push(value.clone()); - } - } - } - cloned - } -} -impl Default for ArenaMap { - #[inline] - fn default() -> Self { - Self::new() - } -} -impl ArenaMap { - /// Creates a new, empty ArenaMap - pub fn new() -> Self { - Self { - arena: Arena::default(), - keys: vec![], - _marker: core::marker::PhantomData, - } - } - - /// Returns true if this [ArenaMap] is empty - pub fn is_empty(&self) -> bool { - self.keys.is_empty() - } - - /// Returns the total number of actively linked items in the map - pub fn len(&self) -> usize { - self.keys.iter().filter(|item| item.is_some()).count() - } - - /// Returns true if this map contains `key` - pub fn contains(&self, key: K) -> bool { - self.keys - .get(key.index()) - .map(|item| item.is_some()) - .unwrap_or(false) - } - - /// Adds a new entry to the map, returning the key it is associated to - pub fn push(&mut self, value: V) -> K { - let key = self.alloc_key(); - self.alloc_node(key, value); - key - } - - /// Used in conjunction with `alloc_key` to associate data with the allocated key - pub fn append(&mut self, key: K, value: V) { - self.alloc_node(key, value); - } - - /// Returns a reference to the value associated with the given key - pub fn get(&self, key: K) -> Option<&V> { - self.keys - .get(key.index()) - .and_then(|item| item.map(|nn| unsafe { nn.as_ref() })) - } - - /// Returns a mutable reference to the value associated with the given key - pub fn get_mut(&mut self, key: K) -> Option<&mut V> { - self.keys - .get_mut(key.index()) - .and_then(|item| item.map(|mut nn| unsafe { nn.as_mut() })) - } - - /// Returns a raw pointer to the value associated with the given key - /// - /// # Safety - /// - /// This function is unsafe, since the resulting pointer could outlive the arena itself, - /// or be used to incorrectly alias a value for which a mutable reference exists. - /// - /// To safely use this function, callers must never construct a reference from the pointer - /// unless they can guarantee that the data pointed to is immutable, or can be safely accessed - /// using atomic operations. No other uses are permitted, unless you want to shoot yourself - /// in the foot. - pub unsafe fn get_raw(&self, key: K) -> Option> { - self.keys.get(key.index()).copied().and_then(|item| item) - } - - /// Takes the value that was stored at the given key - pub fn take(&mut self, key: K) -> Option> { - self.keys[key.index()].take() - } - - pub fn iter(&self) -> impl Iterator>> + '_ { - self.keys.iter().copied() - } - - /// Removes the value associated with the given key - /// - /// NOTE: This function will panic if the key is invalid/unbound - pub fn remove(&mut self, key: K) { - self.keys[key.index()].take(); - } - - pub fn alloc_key(&mut self) -> K { - let id = self.keys.len(); - let key = K::new(id); - self.keys.push(None); - key - } - - fn alloc_node(&mut self, key: K, value: V) -> NonNull { - let len = key.index() + 1; - if len > self.keys.len() { - self.keys.resize(len, None); - } - let value = self.arena.alloc(value); - let nn = unsafe { NonNull::new_unchecked(value) }; - self.keys[key.index()].replace(nn); - nn - } -} -impl Index for ArenaMap { - type Output = V; - - #[inline] - fn index(&self, index: K) -> &Self::Output { - self.get(index).unwrap() - } -} -impl IndexMut for ArenaMap { - #[inline] - fn index_mut(&mut self, index: K) -> &mut Self::Output { - self.get_mut(index).unwrap() - } -} - -/// OrderedArenaMap is an extension of ArenaMap that provides for arbitrary ordering of keys/values -/// -/// This is done using an intrusive linked list alongside an ArenaMap. The list is used to link one -/// key/value pair to the next, so any ordering you wish to implement is possible. This is particularly -/// useful for layout of blocks in a function, or instructions within blocks, as you can precisely position -/// them relative to other blocks/instructions. -/// -/// Because the linked list is intrusive, it is virtually free in terms of space, but comes with the -/// standard overhead for traversals. That said, there are a couple of niceties that give it good overall -/// performance: -/// -/// * It is a doubly-linked list, so you can traverse equally efficiently front-to-back or back-to-front, -/// * It has O(1) indexing; given a key, we can directly obtain a reference to a node, and with that, -/// obtain a cursor over the list starting at that node. -pub struct OrderedArenaMap { - list: LinkedList>, - map: ArenaMap>, -} -impl Drop for OrderedArenaMap { - fn drop(&mut self) { - self.list.fast_clear(); - } -} -impl Clone for OrderedArenaMap { - fn clone(&self) -> Self { - let mut cloned = Self::new(); - for opt in self.map.iter() { - match opt { - None => { - cloned.map.alloc_key(); - } - Some(nn) => { - let value = unsafe { nn.as_ref() }.value(); - cloned.push(value.clone()); - } - } - } - cloned - } -} -impl Default for OrderedArenaMap { - #[inline] - fn default() -> Self { - Self::new() - } -} -impl OrderedArenaMap { - pub fn new() -> Self { - Self { - map: ArenaMap::new(), - list: LinkedList::new(LayoutAdapter::new()), - } - } - - /// Returns true if this [OrderedArenaMap] is empty - pub fn is_empty(&self) -> bool { - self.list.is_empty() - } - - /// Returns the total number of actively linked items in the map - pub fn len(&self) -> usize { - self.list.iter().count() - } - - /// Returns true if this map contains the given key and its value has been linked - #[inline] - pub fn contains(&self, key: K) -> bool { - if let Some(node) = self.map.get(key) { - node.link.is_linked() - } else { - false - } - } - - /// Returns a reference to the value associated with the given key, if present and linked - pub fn get(&self, key: K) -> Option<&V> { - let node = self.map.get(key)?; - if node.link.is_linked() { - Some(node.value()) - } else { - None - } - } - - /// Returns a mutable reference to the value associated with the given key, if present and linked - pub fn get_mut(&mut self, key: K) -> Option<&mut V> { - let node = self.map.get_mut(key)?; - if node.link.is_linked() { - Some(node.value_mut()) - } else { - None - } - } - - /// Allocates a key, but does not link the data - #[inline] - pub fn create(&mut self) -> K { - self.map.alloc_key() - } - - /// Used with `create` when ready to associate data with the allocated key, linking it in to the end of the list - pub fn append(&mut self, key: K, value: V) { - debug_assert!(!self.contains(key)); - let data = self.alloc_node(key, value); - self.list.push_back(data); - } - - /// Like `append`, but inserts the node before `before` in the list - /// - /// NOTE: This function will panic if `before` is not present in the list - pub fn insert_before(&mut self, key: K, before: K, value: V) { - let value_opt = self.get_mut(key); - debug_assert!(value_opt.is_none()); - let data = self.alloc_node(key, value); - let mut cursor = self.cursor_mut_at(before); - cursor.insert_before(data); - } - - /// Like `append`, but inserts the node after `after` in the list - /// - /// NOTE: This function will panic if `after` is not present in the list - pub fn insert_after(&mut self, key: K, after: K, value: V) { - let value_opt = self.get_mut(key); - debug_assert!(value_opt.is_none()); - let data = self.alloc_node(key, value); - let mut cursor = self.cursor_mut_at(after); - cursor.insert_after(data); - } - - /// Allocates a key and links data in the same operation - pub fn push(&mut self, value: V) -> K { - let key = self.alloc_key(); - self.append(key, value); - key - } - - /// Like `push`, but inserts the node after `after` in the list - /// - /// NOTE: This function will panic if `after` is not present in the list - pub fn push_after(&mut self, after: K, value: V) -> K { - let key = self.alloc_key(); - self.insert_after(key, after, value); - key - } - - /// Unlinks the value associated with the given key from this map - /// - /// NOTE: Removal does not result in deallocation of the underlying data, this - /// happens when the map is dropped. To perform early garbage collection, you can - /// clone the map, and drop the original. - pub fn remove(&mut self, key: K) { - if let Some(value) = self.map.get(key) { - assert!( - value.link.is_linked(), - "cannot remove a value that is not linked" - ); - let mut cursor = unsafe { self.list.cursor_mut_from_ptr(value) }; - cursor.remove(); - } - } - - /// Returns the first node in the map - pub fn first(&self) -> Option<&LayoutNode> { - self.list.front().get() - } - - /// Returns the last node in the map - pub fn last(&self) -> Option<&LayoutNode> { - self.list.back().get() - } - - /// Returns a cursor which can be used to traverse the map in order (front to back) - pub fn cursor(&self) -> Cursor<'_, LayoutAdapter> { - self.list.front() - } - - /// Returns a cursor which can be used to traverse the map mutably, in order (front to back) - pub fn cursor_mut(&mut self) -> CursorMut<'_, LayoutAdapter> { - self.list.front_mut() - } - - /// Returns a cursor which can be used to traverse the map in order (front to back), starting - /// at the key given. - pub fn cursor_at(&self, key: K) -> Cursor<'_, LayoutAdapter> { - let ptr = &self.map[key] as *const LayoutNode; - unsafe { self.list.cursor_from_ptr(ptr) } - } - - /// Returns a cursor which can be used to traverse the map mutably, in order (front to back), starting - /// at the key given. - pub fn cursor_mut_at(&mut self, key: K) -> CursorMut<'_, LayoutAdapter> { - let ptr = &self.map[key] as *const LayoutNode; - unsafe { self.list.cursor_mut_from_ptr(ptr) } - } - - /// Returns an iterator over the key/value pairs in the map. - /// - /// The iterator is double-ended, so can be used to traverse the map front-to-back, or back-to-front - pub fn iter(&self) -> OrderedArenaMapIter<'_, K, V> { - OrderedArenaMapIter(self.list.iter()) - } - - /// Returns an iterator over the keys in the map, in order (front to back) - pub fn keys(&self) -> impl Iterator + '_ { - self.list.iter().map(|item| item.key()) - } - - /// Returns an iterator over the values in the map, in order (front to back) - pub fn values(&self) -> impl Iterator { - self.list.iter().map(|item| item.value()) - } - - #[inline] - fn alloc_key(&mut self) -> K { - self.map.alloc_key() - } - - fn alloc_node(&mut self, key: K, value: V) -> UnsafeRef> { - let nn = self.map.alloc_node(key, LayoutNode::new(key, value)); - unsafe { UnsafeRef::from_raw(nn.as_ptr()) } - } -} -impl Index for OrderedArenaMap { - type Output = V; - - #[inline] - fn index(&self, index: K) -> &Self::Output { - self.map[index].value() - } -} -impl IndexMut for OrderedArenaMap { - #[inline] - fn index_mut(&mut self, index: K) -> &mut Self::Output { - self.map.get_mut(index).unwrap().value_mut() - } -} - -pub struct OrderedArenaMapIter<'a, K, V>( - intrusive_collections::linked_list::Iter<'a, LayoutAdapter>, -) -where - K: EntityRef; -impl<'a, K, V> Iterator for OrderedArenaMapIter<'a, K, V> -where - K: EntityRef, -{ - type Item = (K, &'a V); - - #[inline] - fn next(&mut self) -> Option { - self.0.next().map(|item| (item.key(), item.value())) - } -} -impl<'a, K, V> DoubleEndedIterator for OrderedArenaMapIter<'a, K, V> -where - K: EntityRef, -{ - #[inline] - fn next_back(&mut self) -> Option { - self.0.next_back().map(|item| (item.key(), item.value())) - } -} diff --git a/hir/src/lib.rs b/hir/src/lib.rs deleted file mode 100644 index fd02ecee3..000000000 --- a/hir/src/lib.rs +++ /dev/null @@ -1,247 +0,0 @@ -#![deny(warnings)] -// Temporary until 1.76 is released -#![allow(stable_features)] -// TODO: Stabilized in 1.76, not yet released -// Required for pass infrastructure, can be removed when it gets stabilized -// in an upcoming release, see https://github.com/rust-lang/rust/issues/65991 -#![feature(trait_upcasting)] -pub mod parser; - -#[macro_use] -extern crate lalrpop_util; - -pub use intrusive_collections::UnsafeRef; -pub use miden_diagnostics::SourceSpan; -pub use miden_hir_macros::*; -pub use miden_hir_symbol::{symbols, Symbol}; -pub use miden_hir_type::{AddressSpace, Alignable, FunctionType, StructType, Type}; -pub use winter_math::{FieldElement, StarkField}; - -/// Represents a field element in Miden -pub type Felt = winter_math::fields::f64::BaseElement; - -/// Represents an offset from the base of linear memory in Miden -pub type Offset = u32; - -#[macro_export] -macro_rules! assert_matches { - ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => { - match $left { - $( $pattern )|+ $( if $guard )? => {} - ref left_val => { - panic!(r#" -assertion failed: `(left matches right)` - left: `{:?}`, - right: `{}`"#, left_val, stringify!($($pattern)|+ $(if $guard)?)); - } - } - }; - - ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $msg:literal $(,)?) => { - match $left { - $( $pattern )|+ $( if $guard )? => {} - ref left_val => { - panic!(concat!(r#" -assertion failed: `(left matches right)` - left: `{:?}`, - right: `{}` -"#, $msg), left_val, stringify!($($pattern)|+ $(if $guard)?)); - } - } - }; - - ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $msg:literal, $($arg:tt)+) => { - match $left { - $( $pattern )|+ $( if $guard )? => {} - ref left_val => { - panic!(concat!(r#" -assertion failed: `(left matches right)` - left: `{:?}`, - right: `{}` -"#, $msg), left_val, stringify!($($pattern)|+ $(if $guard)?), $($arg)+); - } - } - } -} - -#[macro_export] -macro_rules! diagnostic { - ($diagnostics:ident, $severity:expr, $msg:literal) => {{ - $diagnostics.diagnostic($severity).with_message($msg).emit(); - }}; - - ($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: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: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: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(); - } - }}; -} - -pub mod adt; -mod asm; -mod attribute; -mod block; -mod builder; -mod constants; -mod dataflow; -mod display; -mod function; -mod globals; -mod ident; -mod immediates; -mod insert; -mod instruction; -mod layout; -mod locals; -mod module; -pub mod pass; -mod program; -mod segments; -pub mod testing; -#[cfg(test)] -mod tests; -mod value; -mod write; - -pub use self::asm::*; -pub use self::attribute::{attributes, Attribute, AttributeSet, AttributeValue}; -pub use self::block::{Block, BlockData}; -pub use self::builder::{ - DefaultInstBuilder, FunctionBuilder, InstBuilder, InstBuilderBase, ReplaceBuilder, -}; -pub use self::constants::{Constant, ConstantData, ConstantPool, IntoBytes}; -pub use self::dataflow::DataFlowGraph; -pub use self::display::{Decorator, DisplayValues}; -pub use self::function::*; -pub use self::globals::*; -pub use self::ident::{FunctionIdent, Ident}; -pub use self::immediates::Immediate; -pub use self::insert::{Insert, InsertionPoint}; -pub use self::instruction::*; -pub use self::layout::{ArenaMap, LayoutAdapter, LayoutNode, OrderedArenaMap}; -pub use self::locals::{Local, LocalId}; -pub use self::module::*; -pub use self::pass::{ - AnalysisKey, ConversionPassRegistration, ModuleRewritePassAdapter, PassInfo, - RewritePassRegistration, -}; -pub use self::program::{Linker, LinkerError, Program, ProgramAnalysisKey, ProgramBuilder}; -pub use self::segments::{DataSegment, DataSegmentAdapter, DataSegmentError, DataSegmentTable}; -pub use self::value::{Value, ValueData, ValueList, ValueListPool}; -pub use self::write::{write_external_function, write_function, write_instruction}; - -// Re-export cranelift_entity so that users don't have to hunt for the same version -pub use cranelift_entity; - -use core::fmt; - -/// A `ProgramPoint` represents a position in a function where the live range of an SSA value can -/// begin or end. It can be either: -/// -/// 1. An instruction or -/// 2. A block header. -/// -/// This corresponds more or less to the lines in the textual form of the IR. -#[derive(PartialEq, Eq, Clone, Copy, Hash)] -pub enum ProgramPoint { - /// An instruction in the function. - Inst(Inst), - /// A block header. - Block(Block), -} -impl ProgramPoint { - /// Get the instruction we know is inside. - pub fn unwrap_inst(self) -> Inst { - match self { - Self::Inst(x) => x, - Self::Block(x) => panic!("expected inst: {}", x), - } - } -} -impl From for ProgramPoint { - fn from(inst: Inst) -> Self { - Self::Inst(inst) - } -} -impl From for ProgramPoint { - fn from(block: Block) -> Self { - Self::Block(block) - } -} -impl fmt::Display for ProgramPoint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Self::Inst(x) => write!(f, "{}", x), - Self::Block(x) => write!(f, "{}", x), - } - } -} -impl fmt::Debug for ProgramPoint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ProgramPoint({})", self) - } -} diff --git a/hir/src/locals.rs b/hir/src/locals.rs deleted file mode 100644 index 5e69dad77..000000000 --- a/hir/src/locals.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::{alloc::Layout, fmt}; - -use super::Type; - -/// A strongly typed identifier for referencing locals associated with a function -#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct LocalId(u8); -impl LocalId { - /// Create a new instance from a `u32`. - #[inline] - pub fn from_u8(x: u8) -> Self { - debug_assert!(x < u8::MAX, "invalid raw local id"); - Self(x) - } - - /// Return the underlying index value as a `usize`. - #[inline] - pub fn as_usize(self) -> usize { - self.0 as usize - } -} -impl cranelift_entity::EntityRef for LocalId { - #[inline] - fn new(index: usize) -> Self { - debug_assert!(index < (u8::MAX as usize)); - Self(index as u8) - } - - #[inline] - fn index(self) -> usize { - self.0 as usize - } -} -impl cranelift_entity::packed_option::ReservedValue for LocalId { - #[inline] - fn reserved_value() -> LocalId { - Self(u8::MAX) - } - - #[inline] - fn is_reserved_value(&self) -> bool { - self.0 == u8::MAX - } -} -impl fmt::Display for LocalId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "local{}", self.0) - } -} -impl fmt::Debug for LocalId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -/// Represents a local allocated on the heap statically -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Local { - /// The unique identifier associated with this local - /// - /// It also represents the offset in the set of locals of a function - /// where this local will be allocated. - /// - /// NOTE: If a local's size is larger than a word, multiple consecutive - /// local allocations may be made to ensure there is enough memory starting - /// at the offset represented by `id` to hold the entire value - pub id: LocalId, - /// The type of the value stored in this local - pub ty: Type, -} -impl Local { - /// Returns the [Layout] for this local in memory - pub fn layout(&self) -> Layout { - self.ty.layout() - } - - /// Returns the size in bytes for this local, including necessary alignment padding - pub fn size_in_bytes(&self) -> usize { - self.ty.size_in_bytes() - } - - /// Returns the size in words for this local, including necessary alignment padding - pub fn size_in_words(&self) -> usize { - self.ty.size_in_words() - } -} diff --git a/hir/src/module.rs b/hir/src/module.rs deleted file mode 100644 index 1aed37cad..000000000 --- a/hir/src/module.rs +++ /dev/null @@ -1,863 +0,0 @@ -use std::{collections::BTreeMap, fmt}; - -use intrusive_collections::{ - intrusive_adapter, - linked_list::{Cursor, CursorMut}, - LinkedList, RBTreeLink, -}; -use miden_diagnostics::{DiagnosticsHandler, Severity, Spanned}; -use rustc_hash::FxHashSet; - -use super::{pass::AnalysisKey, *}; - -/// 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); - -intrusive_adapter!(pub ModuleTreeAdapter = Box: Module { link: RBTreeLink }); -impl<'a> intrusive_collections::KeyAdapter<'a> for ModuleTreeAdapter { - type Key = Ident; - - #[inline] - fn get_key(&self, module: &'a Module) -> Ident { - module.name - } -} - -/// Represents a SSA IR module -/// -/// These correspond to MASM modules -/// This module is largely a container for functions, but it also provides -/// as the owner for pooled resources available to functions: -/// -/// * Mapping from Signature to FuncRef -/// * Mapping from FunctionName to FuncRef -#[derive(Spanned, AnalysisKey)] -pub struct Module { - /// The link used to attach this module to a [Program] - link: RBTreeLink, - /// The name of this module - #[span] - #[analysis_key] - pub name: Ident, - /// Documentation attached to this module, to be passed through to - /// Miden Assembly during code generation. - pub docs: Option, - /// The set of data segments allocated in this module - pub(crate) segments: DataSegmentTable, - /// The set of global variables declared in this module - pub(crate) globals: GlobalVariableTable, - /// The set of functions which belong to this module, in the order - /// in which they were defined. - pub(crate) functions: LinkedList, - /// This flag indicates whether this module is a kernel module - /// - /// Kernel modules have additional constraints imposed on them that regular - /// modules do not, in exchange for some useful functionality: - /// - /// * Functions with external linkage are required to use the `Kernel` calling convention. - /// * A kernel module executes in the root context of the Miden VM, allowing one to expose functionality - /// that is protected from tampering by other non-kernel functions in the program. - /// * Due to the above, you may not reference globals outside the kernel module, from within - /// kernel functions, as they are not available in the root context. - is_kernel: bool, -} -impl fmt::Display for Module { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use crate::write::DisplayIdent; - use std::fmt::Write; - - if self.is_kernel { - writeln!(f, "kernel {}\n", DisplayIdent(&self.name))?; - } else { - writeln!(f, "module {}\n", DisplayIdent(&self.name))?; - } - - let has_segments = !self.segments.is_empty(); - let has_globals = !self.globals.is_empty(); - let has_constants = self.globals.has_constants(); - - if has_segments { - f.write_str("memory {\n")?; - for segment in self.segments.iter() { - writeln!( - f, - " segment @{:#x} x {} = {};", - segment.offset(), - segment.size(), - segment.init(), - )?; - } - f.write_str("}\n")?; - } - - if has_globals { - if has_constants { - if has_segments { - f.write_char('\n')?; - } - for (constant, constant_data) in self.globals.constants() { - let id = constant.as_u32(); - writeln!(f, "const ${id} = {constant_data};")?; - } - - f.write_char('\n')?; - } - - for global in self.globals.iter() { - write!( - f, - "global {} @{} : {}", - global.linkage, - DisplayIdent(&global.name), - global.ty - )?; - match global.init { - Some(init) => { - writeln!( - f, - " = ${} {{ id = {} }};", - init.as_u32(), - global.id().as_u32() - )?; - } - None => { - writeln!(f, " {{ id = {} }};", global.id().as_u32())?; - } - } - } - f.write_char('\n')?; - } - - let mut external_functions = BTreeMap::::default(); - for (i, function) in self.functions.iter().enumerate() { - for import in function.dfg.imports() { - // Don't print declarations for functions in this module - if import.id.module == self.name { - continue; - } - external_functions - .entry(import.id) - .or_insert_with(|| import.signature.clone()); - } - if i > 0 { - writeln!(f)?; - } - write_function(f, function)?; - } - - if !external_functions.is_empty() { - writeln!(f)?; - - for (id, sig) in external_functions.iter() { - writeln!(f)?; - write_external_function(f, id, sig)?; - } - } - - Ok(()) - } -} -impl fmt::Debug for Module { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Module") - .field("name", &self.name) - .field("is_kernel", &self.is_kernel) - .field("docs", &self.docs) - .field("segments", &self.segments) - .field("globals", &self.globals) - .field("functions", &self.functions) - .finish() - } -} -impl midenc_session::Emit for Module { - fn name(&self) -> Option { - Some(self.name.as_symbol()) - } - fn output_type(&self) -> midenc_session::OutputType { - midenc_session::OutputType::Hir - } - fn write_to(&self, mut writer: W) -> std::io::Result<()> { - writer.write_fmt(format_args!("{}", self)) - } -} -impl Eq for Module {} -impl PartialEq for Module { - fn eq(&self, other: &Self) -> bool { - let is_eq = self.name == other.name - && self.is_kernel == other.is_kernel - && self.docs == other.docs - && self.segments.iter().eq(other.segments.iter()) - && self.globals.len() == other.globals.len() - && self.functions.iter().count() == other.functions.iter().count(); - if !is_eq { - return false; - } - - for global in self.globals.iter() { - let id = global.id(); - if !other.globals.contains_key(id) { - return false; - } - let other_global = other.globals.get(id); - if global != other_global { - return false; - } - } - - for function in self.functions.iter() { - if !other.contains(function.id.function) { - return false; - } - if let Some(other_function) = other.function(function.id.function) { - if function != other_function { - return false; - } - } else { - return false; - } - } - - true - } -} - -/// This macro asserts that a function is valid for insertion into a given module. -macro_rules! assert_valid_function { - ($module:ident, $function:ident) => { - assert_eq!($module.name, $function.id.module, "mismatched module identifiers"); - assert!( - $function.is_detached(), - "cannot attach a function to a module that is already attached to a module" - ); - // Validate the kernel rules - if $function.is_kernel() { - assert!($module.is_kernel, "cannot add kernel functions to a non-kernel module"); - } else if $module.is_kernel && $function.is_public() { - panic!("functions with external linkage in kernel modules must use the kernel calling convention"); - } - } -} - -impl Module { - /// Create a new, empty [Module] - pub fn new>(name: S) -> Self { - Self::make(name.into(), /*is_kernel=*/ false) - } - - /// Create a new, empty [Module] with the given source location - pub fn new_with_span>(name: S, span: SourceSpan) -> Self { - let name = Ident::new(Symbol::intern(name.as_ref()), span); - Self::make(name, /*is_kernel=*/ false) - } - - /// Create a new, empty kernel [Module] - pub fn new_kernel>(name: S) -> Self { - Self::make(name.into(), /*is_kernel=*/ true) - } - - /// Create a new, empty kernel [Module] with the given source location - pub fn new_kernel_with_span>(name: S, span: SourceSpan) -> Self { - let name = Ident::new(Symbol::intern(name.as_ref()), span); - Self::make(name, /*is_kernel=*/ true) - } - - fn make(name: Ident, is_kernel: bool) -> Self { - Self { - link: Default::default(), - name, - docs: None, - segments: Default::default(), - globals: GlobalVariableTable::new(ConflictResolutionStrategy::None), - functions: Default::default(), - is_kernel, - } - } - - /// Returns true if this module is a kernel module - #[inline] - pub const fn is_kernel(&self) -> bool { - self.is_kernel - } - - /// Returns true if this module has yet to be attached to a [Program] - pub fn is_detached(&self) -> bool { - !self.link.is_linked() - } - - /// Return the table of data segments for this module - pub fn segments(&self) -> &DataSegmentTable { - &self.segments - } - - /// Declare a new [DataSegment] in this module, with the given offset, size, and data. - /// - /// Returns `Err` if the segment declaration is invalid, or conflicts with an existing segment - /// - /// Data segments are ordered by the address at which they are allocated, at link-time, all - /// segments from all modules are linked together, and they must either be disjoint, or exactly - /// identical in order to overlap - it is not permitted to have partially overlapping segments - /// with different views of the memory represented by that segment. - pub fn declare_data_segment( - &mut self, - offset: Offset, - size: u32, - init: ConstantData, - readonly: bool, - ) -> Result<(), DataSegmentError> { - self.segments.declare(offset, size, init, readonly) - } - - /// Return the table of global variables for this module - pub fn globals(&self) -> &GlobalVariableTable { - &self.globals - } - - /// Declare a new [GlobalVariable] in this module, with the given name, type, linkage, and optional initializer. - /// - /// Returns `Err` if a symbol with the same name but conflicting declaration already exists, - /// or if the specification of the global variable is invalid in any way. - /// - /// NOTE: The [GlobalVariable] returned here is scoped to this module only, it cannot be used to - /// index into the global variable table of a [Program], which is constructed at link-time. - pub fn declare_global_variable( - &mut self, - name: Ident, - ty: Type, - linkage: Linkage, - init: Option, - ) -> Result { - self.globals.declare(name, ty, linkage, init) - } - - /// Set the initializer for a [GlobalVariable] to `init`. - /// - /// Returns `Err` if the initializer conflicts with the current definition of the global in any way. - pub fn set_global_initializer( - &mut self, - gv: GlobalVariable, - init: ConstantData, - ) -> Result<(), GlobalVariableError> { - self.globals.set_initializer(gv, init) - } - - /// Get the data associated with the given [GlobalVariable] - #[inline] - pub fn global(&self, id: GlobalVariable) -> &GlobalVariableData { - self.globals.get(id) - } - - /// Look up a global by `name`. - pub fn find_global(&self, name: Ident) -> Option<&GlobalVariableData> { - self.globals.find(name).map(|gv| self.globals.get(gv)) - } - - /// Find the first function in this module marked with the `entrypoint` attribute - pub fn entrypoint(&self) -> Option { - self.functions.iter().find_map(|f| { - if f.has_attribute(&symbols::Entrypoint) { - Some(f.id) - } else { - None - } - }) - } - - /// Return an iterator over the functions in this module - /// - /// The iterator is double-ended, so can be used to traverse the module body in either direction - pub fn functions<'a, 'b: 'a>( - &'b self, - ) -> intrusive_collections::linked_list::Iter<'a, FunctionListAdapter> { - self.functions.iter() - } - - /// Get a [Function] in this module by name, if available - pub fn function<'a, 'b: 'a>(&'b self, id: Ident) -> Option<&'a Function> { - self.cursor_at(id).get() - } - - /// Compute the set of imports for this module, automatically aliasing modules when there - /// are namespace conflicts - pub fn imports(&self) -> ModuleImportInfo { - let mut imports = ModuleImportInfo::default(); - let locals = self - .functions - .iter() - .map(|f| f.id) - .collect::>(); - - for function in self.functions.iter() { - for import in function.imports() { - if !locals.contains(&import.id) { - imports.add(import.id); - } - } - } - imports - } - - /// Returns true if this module contains the function `name` - pub fn contains(&self, name: Ident) -> bool { - self.function(name).is_some() - } - - /// Unlinks the given function from this module - pub fn unlink(&mut self, id: Ident) -> Box { - let mut cursor = self.cursor_mut_at(id); - cursor.remove().expect("invalid function id") - } - - /// Append `function` to the end of this module's body, returning the [FuncId] - /// assigned to it within this module. - /// - /// NOTE: This function will panic if either of the following rules are violated: - /// - /// * If this module is a kernel module, public functions must use the kernel calling convention, - /// however private functions can use any convention. - /// * If this module is not a kernel module, functions may not use the kernel calling convention - pub fn push(&mut self, function: Box) -> Result<(), SymbolConflictError> { - assert_valid_function!(self, function); - if let Some(prev) = self.function(function.id.function) { - return Err(SymbolConflictError(prev.id)); - } - self.functions.push_back(function); - Ok(()) - } - - /// Insert `function` in the module body before the function with id `before` - /// - /// If `before` is no longer attached to this module, `function` is added to - /// the end of the module body. - pub fn insert_before( - &mut self, - function: Box, - before: Ident, - ) -> Result<(), SymbolConflictError> { - assert_valid_function!(self, function); - if let Some(prev) = self.function(function.id.function) { - return Err(SymbolConflictError(prev.id)); - } - - let mut cursor = self.cursor_mut_at(before); - cursor.insert_before(function); - - Ok(()) - } - - /// Insert `function` in the module body after the function with id `after` - /// - /// If `after` is no longer attached to this module, `function` is added to - /// the end of the module body. - pub fn insert_after( - &mut self, - function: Box, - after: Ident, - ) -> Result<(), SymbolConflictError> { - assert_valid_function!(self, function); - if let Some(prev) = self.function(function.id.function) { - return Err(SymbolConflictError(prev.id)); - } - - let mut cursor = self.cursor_mut_at(after); - if cursor.is_null() { - cursor.insert_before(function); - } else { - cursor.insert_after(function); - } - - Ok(()) - } - - /// Remove the first function in the module, and return it, if present - pub fn pop_front(&mut self) -> Option> { - self.functions.pop_front() - } - - /// Returns a mutable cursor to the module body, starting at the first function. - /// - /// If the module body is empty, the returned cursor will point to the null object. - /// - /// NOTE: If one uses this cursor to insert a function that is invalid - #[inline] - pub fn cursor_mut<'a, 'b: 'a>(&'b mut self) -> ModuleCursor<'a> { - ModuleCursor { - cursor: self.functions.front_mut(), - name: self.name, - is_kernel: self.is_kernel, - } - } - - /// Returns a cursor to the module body, located at the function indicated by `id`. - /// - /// If no function with `id` is in the list, the returned cursor will point to the null object. - pub fn cursor_at<'a, 'b: 'a>(&'b self, id: Ident) -> Cursor<'a, FunctionListAdapter> { - let mut cursor = self.functions.front(); - while let Some(function) = cursor.get() { - if function.id.function == id { - break; - } - cursor.move_next(); - } - cursor - } - - /// Returns a mutable cursor to the module body, located at the function indicated by `id`. - /// - /// If no function with `id` is in the list, the returned cursor will point to the null object. - pub fn cursor_mut_at<'a, 'b: 'a>(&'b mut self, id: Ident) -> ModuleCursor<'a> { - let mut cursor = self.functions.front_mut(); - while let Some(function) = cursor.get() { - if function.id.function == id { - break; - } - cursor.move_next(); - } - ModuleCursor { - cursor, - name: self.name, - is_kernel: self.is_kernel, - } - } -} - -pub struct ModuleCursor<'a> { - cursor: CursorMut<'a, FunctionListAdapter>, - name: Ident, - is_kernel: bool, -} -impl<'a> ModuleCursor<'a> { - /// Returns true if this cursor is pointing to the null object - #[inline(always)] - pub fn is_null(&self) -> bool { - self.cursor.is_null() - } - - /// Return a reference to the function pointed to by this cursor - /// - /// If the cursor is pointing to the null object, `None` is returned - #[inline(always)] - pub fn get(&self) -> Option<&Function> { - self.cursor.get() - } - - /// Insert a new function into the module after the cursor. - /// - /// If the cursor is pointing to the null object, the insert happens at the front of the list. - /// - /// NOTE: This function will panic if the function violates the validation rules for - /// the module, i.e. must not be attached, follows kernel module rules when applicable. - pub fn insert_after(&mut self, function: Box) { - assert_valid_function!(self, function); - self.cursor.insert_after(function); - } - - /// Insert a new function into the module before the cursor. - /// - /// If the cursor is pointing to the null object, the insert happens at the end of the list. - /// - /// NOTE: This function will panic if the function violates the validation rules for - /// the module, i.e. must not be attached, follows kernel module rules when applicable. - pub fn insert_before(&mut self, function: Box) { - assert_valid_function!(self, function); - self.cursor.insert_before(function); - } - - /// Moves this cursor to the next function in the module. - /// - /// If the cursor is pointing to the null object, then this moves the cursor to the front - /// of the list. If at the end of the list, it moves to the null object. - #[inline(always)] - pub fn move_next(&mut self) { - self.cursor.move_next(); - } - - /// Moves this cursor to the previous function in the module. - /// - /// If the cursor is pointing to the null object, then this moves the cursor to the end - /// of the list. If at the front of the list, it moves to the null object. - #[inline(always)] - pub fn move_prev(&mut self) { - self.cursor.move_prev(); - } - - /// Return a cursor pointing to the next function in the module. - /// - /// If this cursor is on the null object, then the returned cursor will be on the - /// front of the list. If at the last element, then the returned cursor will on the - /// null object. - #[inline(always)] - pub fn peek_next(&self) -> Cursor<'_, FunctionListAdapter> { - self.cursor.peek_next() - } - - /// Return a cursor pointing to the previous function in the module. - /// - /// If this cursor is on the null object, then the returned cursor will be on the - /// end of the list. If at the first element, then the returned cursor will on the - /// null object. - #[inline(always)] - pub fn peek_prev(&self) -> Cursor<'_, FunctionListAdapter> { - self.cursor.peek_prev() - } - - /// Removes the current function from the module. - /// - /// The cursor will be moved to the next function in the module, or the null object - /// if we're at the end of the module. - #[inline(always)] - pub fn remove(&mut self) -> Option> { - self.cursor.remove() - } -} - -pub struct ModuleBuilder { - module: Box, -} -impl From> for ModuleBuilder { - fn from(module: Box) -> Self { - Self { module } - } -} -impl ModuleBuilder { - pub fn new>(name: S) -> Self { - Self { - module: Box::new(Module::new(name)), - } - } - - pub fn new_kernel>(name: S) -> Self { - Self { - module: Box::new(Module::new_kernel(name)), - } - } - - pub fn with_span(&mut self, span: SourceSpan) -> &mut Self { - self.module.name = Ident::new(self.module.name.as_symbol(), span); - self - } - - pub fn with_docs>(&mut self, docs: S) -> &mut Self { - self.module.docs = Some(docs.into()); - self - } - - pub fn name(&self) -> Ident { - self.module.name - } - - pub fn declare_global_variable>( - &mut self, - name: S, - ty: Type, - linkage: Linkage, - init: Option, - span: SourceSpan, - ) -> Result { - let name = Ident::new(Symbol::intern(name.as_ref()), span); - self.module.declare_global_variable(name, ty, linkage, init) - } - - pub fn set_global_initializer( - &mut self, - gv: GlobalVariable, - init: ConstantData, - ) -> Result<(), GlobalVariableError> { - self.module.set_global_initializer(gv, init) - } - - pub fn declare_data_segment>( - &mut self, - offset: Offset, - size: u32, - init: I, - readonly: bool, - ) -> Result<(), DataSegmentError> { - self.module - .declare_data_segment(offset, size, init.into(), readonly) - } - - /// Start building a new function in this module - pub fn function<'a, 'b: 'a, S: Into>( - &'b mut self, - name: S, - signature: Signature, - ) -> Result, SymbolConflictError> { - let name = name.into(); - if let Some(prev) = self.module.function(name) { - return Err(SymbolConflictError(prev.id)); - } - - let id = FunctionIdent { - module: self.module.name, - function: name, - }; - let function = Box::new(Function::new(id, signature)); - let entry = function.dfg.entry; - - Ok(ModuleFunctionBuilder { - builder: self, - function, - position: entry, - }) - } - - pub fn build(self) -> Box { - self.module - } -} - -pub struct ModuleFunctionBuilder<'m> { - builder: &'m mut ModuleBuilder, - function: Box, - position: Block, -} -impl<'m> ModuleFunctionBuilder<'m> { - pub fn with_span(&mut self, span: SourceSpan) -> &mut Self { - self.function.id.function = Ident::new(self.function.id.function.as_symbol(), span); - self - } - - /// Get the fully-qualified name of the underlying function - pub fn id(&self) -> FunctionIdent { - self.function.id - } - - /// Get the signature of the underlying function - pub fn signature(&self) -> &Signature { - &self.function.signature - } - - pub fn module<'a, 'b: 'a>(&'b mut self) -> &'a mut ModuleBuilder { - self.builder - } - - #[inline(always)] - pub fn data_flow_graph(&self) -> &DataFlowGraph { - &self.function.dfg - } - - #[inline(always)] - pub fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { - &mut self.function.dfg - } - - #[inline] - pub fn entry_block(&self) -> Block { - self.function.dfg.entry - } - - #[inline] - pub fn current_block(&self) -> Block { - self.position - } - - #[inline] - pub fn switch_to_block(&mut self, block: Block) { - self.position = block; - } - - pub fn create_block(&mut self) -> Block { - self.data_flow_graph_mut().create_block() - } - - pub fn block_params(&self, block: Block) -> &[Value] { - self.data_flow_graph().block_params(block) - } - - pub fn append_block_param(&mut self, block: Block, ty: Type, span: SourceSpan) -> Value { - self.data_flow_graph_mut() - .append_block_param(block, ty, span) - } - - pub fn inst_results(&self, inst: Inst) -> &[Value] { - self.data_flow_graph().inst_results(inst) - } - - pub fn first_result(&self, inst: Inst) -> Value { - self.data_flow_graph().first_result(inst) - } - - pub fn set_attribute(&mut self, name: impl Into, value: impl Into) { - self.data_flow_graph_mut().set_attribute(name, value); - } - - pub fn import_function( - &mut self, - module: M, - function: F, - signature: Signature, - ) -> Result - where - M: Into, - F: Into, - { - self.function - .dfg - .import_function(module.into(), function.into(), signature) - } - - pub fn ins<'a, 'b: 'a>(&'b mut self) -> DefaultInstBuilder<'a> { - DefaultInstBuilder::new(&mut self.function.dfg, self.position) - } - - pub fn build( - self, - diagnostics: &DiagnosticsHandler, - ) -> Result { - let sig = self.function.signature(); - match sig.linkage { - Linkage::External | Linkage::Internal => (), - linkage => { - 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); - } - } - - let is_kernel_module = self.builder.module.is_kernel; - let is_public = sig.is_public(); - - match sig.cc { - CallConv::Kernel if is_kernel_module => { - if !is_public { - 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") - .emit(); - return Err(InvalidFunctionError); - } - } - CallConv::Kernel => { - diagnostics.diagnostic(Severity::Error) - .with_message(format!("invalid calling convention for function '{}'", &self.function.id)) - .with_note("The 'kernel' calling convention is only allowed in kernel modules, on functions with external linkage") - .emit(); - return Err(InvalidFunctionError); - } - _ if is_kernel_module && is_public => { - diagnostics.diagnostic(Severity::Error) - .with_message(format!("invalid calling convention for function '{}'", &self.function.id)) - .with_note("Functions with external linkage, must use the 'kernel' calling convention when defined in a kernel module") - .emit(); - return Err(InvalidFunctionError); - } - _ => (), - } - - let id = self.function.id; - self.builder.module.functions.push_back(self.function); - - Ok(id) - } -} - -#[derive(Debug)] -pub struct InvalidFunctionError; diff --git a/hir/src/parser/ast/block.rs b/hir/src/parser/ast/block.rs deleted file mode 100644 index 1a7f09951..000000000 --- a/hir/src/parser/ast/block.rs +++ /dev/null @@ -1,42 +0,0 @@ -use core::fmt; - -use super::*; - -/// Represents a basic block in a [FunctionDeclaration] -#[derive(Spanned)] -pub struct Block { - #[span] - pub span: SourceSpan, - pub id: crate::Block, - pub params: Vec, - pub body: Vec, -} -impl Block { - pub fn new( - span: SourceSpan, - id: crate::Block, - params: Vec, - body: Vec, - ) -> Self { - Self { - span, - id, - params, - body, - } - } -} -impl fmt::Debug for Block { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Block") - .field("id", &format_args!("{}", self.id)) - .field("params", &self.params) - .field("body", &self.body) - .finish() - } -} -impl PartialEq for Block { - fn eq(&self, other: &Self) -> bool { - self.id == other.id && self.params == other.params && self.body == other.body - } -} diff --git a/hir/src/parser/ast/convert.rs b/hir/src/parser/ast/convert.rs deleted file mode 100644 index 9b1404682..000000000 --- a/hir/src/parser/ast/convert.rs +++ /dev/null @@ -1,930 +0,0 @@ -use std::collections::VecDeque; - -use cranelift_entity::packed_option::ReservedValue; -use intrusive_collections::UnsafeRef; -use miden_diagnostics::{Severity, SourceSpan, Spanned}; -use midenc_session::Session; -use rustc_hash::{FxHashMap, FxHashSet}; - -use crate::parser::ParseError; -use crate::pass::{AnalysisManager, ConversionError, ConversionPass, ConversionResult}; -use crate::{FunctionIdent, Ident, Immediate, Opcode, PassInfo, Type}; - -use super::*; - -/// This pass converts the syntax tree of an HIR module to HIR -#[derive(PassInfo)] -pub struct ConvertAstToHir; -impl ConversionPass for ConvertAstToHir { - type From = Box; - type To = crate::Module; - - fn convert( - &mut self, - mut ast: Self::From, - _analyses: &mut AnalysisManager, - session: &Session, - ) -> ConversionResult { - use std::collections::hash_map::Entry; - - let mut module = if ast.is_kernel { - crate::Module::new_kernel(ast.name) - } else { - crate::Module::new(ast.name) - }; - - let mut is_valid = true; - - // Validate constants - let (constants_by_id, is_constants_valid) = - ast.take_and_validate_constants(&session.diagnostics); - is_valid &= is_constants_valid; - - // Validate globals - let (globals_by_id, is_global_vars_valid) = - ast.take_and_validate_globals(&constants_by_id, &session.diagnostics); - is_valid &= is_global_vars_valid; - - for (_, gv_data) in globals_by_id.into_iter() { - unsafe { - module.globals.insert(gv_data.item); - } - } - - // Validate imports - let (imports_by_id, is_externals_valid) = - ast.take_and_validate_imports(&session.diagnostics); - is_valid &= is_externals_valid; - - // Validate functions - let mut functions_by_id = FxHashMap::::default(); - let mut worklist = Vec::with_capacity(ast.functions.len()); - for function in ast.functions.into_iter() { - match functions_by_id.entry(function.name) { - Entry::Vacant(entry) => { - entry.insert(function.name.span()); - worklist.push(function); - } - Entry::Occupied(entry) => { - let prev = *entry.get(); - session - .diagnostics - .diagnostic(Severity::Error) - .with_message("invalid function declaration") - .with_primary_label( - function.span(), - "a function with the same name has already been declared", - ) - .with_secondary_label(prev, "previously declared here") - .emit(); - is_valid = false; - } - } - } - - let mut functions = crate::FunctionList::default(); - let mut values_by_id = ValuesById::default(); - for mut function in worklist.into_iter() { - values_by_id.clear(); - - let id = FunctionIdent { - module: module.name, - function: function.name, - }; - - is_valid &= function.is_declaration_valid(&session.diagnostics); - let entry = function.blocks[0].id; - let mut blocks_by_id = match function.populate_block_map(&session.diagnostics) { - Ok(blocks) => blocks, - Err(blocks) => { - is_valid = false; - blocks - } - }; - let mut blockq = VecDeque::from([entry]); - - // Build the HIR function - let mut f = Box::new(crate::Function::new_uninit(id, function.signature)); - // Move attributes from the AST to the DFG - f.dfg.attrs = function.attrs; - // The entry block is always the first in the layout - f.dfg.entry = entry; - // Visit each block and build it, but do not yet write to the DataFlowGraph - let mut visited = FxHashSet::::default(); - while let Some(block_id) = blockq.pop_front() { - // Do not visit the same block twice - if !visited.insert(block_id) { - continue; - } - - let mut block_data = crate::BlockData::new(block_id); - let block = blocks_by_id.remove(&block_id).unwrap(); - - // Ensure block parameters are not yet defined - for (num, param) in block.params.into_iter().enumerate() { - match try_insert_param_value( - param.id, - param.span(), - block.id, - num as u16, - param.ty, - &mut values_by_id, - &session.diagnostics, - ) { - Some(id) => { - block_data.params.push(id, &mut f.dfg.value_lists); - } - None => { - is_valid = false; - continue; - } - } - } - - // Populate the block with instructions from the AST - for (num, inst) in block.body.into_iter().enumerate() { - is_valid &= try_insert_inst( - inst, - num as u16, - &mut block_data, - &mut blockq, - &blocks_by_id, - &visited, - &mut values_by_id, - &imports_by_id, - &mut f, - &session.diagnostics, - ); - } - f.dfg.blocks.append(block_id, block_data); - } - - // Now that all of the blocks have been visited, - // we can record the value data in the DataFlowGraph - for (id, data) in core::mem::take(&mut values_by_id).into_iter() { - let next_id = f.dfg.values.next_key(); - let next_raw_id = next_id.as_u32(); - let raw_id = id.as_u32(); - assert!( - raw_id >= next_raw_id, - "expected that {id} is always ahead of, or equal to {next_id}" - ); - if raw_id == next_raw_id { - f.dfg.values.push(data.item); - continue; - } - - // If we reach here, we need to insert dummy data until the next - // key is the one that we have data for - while raw_id > f.dfg.values.next_key().as_u32() { - f.dfg.values.push(crate::ValueData::Inst { - ty: Type::Unknown, - num: 0, - inst: crate::Inst::reserved_value(), - }); - } - assert_eq!(f.dfg.values.push(data.item), id); - } - - functions.push_back(f); - } - - module.functions = functions; - - if is_valid { - Ok(module) - } else { - Err(ConversionError::Failed(ParseError::InvalidModule.into())) - } - } -} - -#[allow(clippy::too_many_arguments)] -fn try_insert_inst( - mut inst: Inst, - num: u16, - block_data: &mut crate::BlockData, - blockq: &mut VecDeque, - blocks_by_id: &BlocksById, - visited_blocks: &FxHashSet, - values_by_id: &mut ValuesById, - imports_by_id: &ImportsById, - function: &mut crate::Function, - diagnostics: &DiagnosticsHandler, -) -> bool { - use std::collections::hash_map::Entry; - - use crate::{BinaryOp, BinaryOpImm, Instruction, PrimOp, PrimOpImm, UnaryOp, UnaryOpImm}; - - let id = function.dfg.insts.alloc_key(); - let span = inst.span; - let results = core::mem::take(&mut inst.outputs); - - // Translate instruction data from AST representation to HIR - let data = match inst.ty { - InstType::BinaryOp { - opcode: op, - overflow, - operands: [Operand::Value(lhs), Operand::Value(rhs)], - } => Some(Instruction::BinaryOp(BinaryOp { - op, - overflow, - args: [rhs.item, lhs.item], - })), - InstType::BinaryOp { - opcode: op, - overflow, - operands: [imm, Operand::Value(rhs)], - } => { - let imm_ty = values_by_id - .get(&rhs) - .map(|v| v.ty().clone()) - .unwrap_or(Type::Unknown); - operand_to_immediate(imm, &imm_ty, diagnostics).map(|imm| { - Instruction::BinaryOpImm(BinaryOpImm { - op, - overflow, - arg: rhs.item, - imm, - }) - }) - } - InstType::BinaryOp { - operands: [_, rhs], .. - } => { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label(rhs.span(), "unexpected immediate operand") - .with_secondary_label( - span, - "only the left-hand operand of a binary operator may be immediate", - ) - .emit(); - None - } - InstType::UnaryOp { - opcode: op, - overflow, - operand: Operand::Value(operand), - } => Some(Instruction::UnaryOp(UnaryOp { - op, - overflow, - arg: operand.item, - })), - InstType::UnaryOp { - opcode: op, - overflow, - operand, - } => { - let imm = match op { - Opcode::ImmI1 => operand_to_immediate(operand, &Type::I1, diagnostics), - Opcode::ImmI8 => operand_to_immediate(operand, &Type::I8, diagnostics), - Opcode::ImmU8 => operand_to_immediate(operand, &Type::U8, diagnostics), - Opcode::ImmI16 => operand_to_immediate(operand, &Type::I16, diagnostics), - Opcode::ImmU16 => operand_to_immediate(operand, &Type::U16, diagnostics), - Opcode::ImmI32 => operand_to_immediate(operand, &Type::I32, diagnostics), - Opcode::ImmU32 => operand_to_immediate(operand, &Type::U32, diagnostics), - Opcode::ImmI64 => operand_to_immediate(operand, &Type::I64, diagnostics), - Opcode::ImmU64 => operand_to_immediate(operand, &Type::U64, diagnostics), - Opcode::ImmF64 => { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label( - span, - "f64 support is not yet implemented in the parser", - ) - .emit(); - None - } - Opcode::ImmFelt => operand_to_immediate(operand, &Type::Felt, diagnostics), - _ => { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label( - span, - "this operation does not support immediate arguments", - ) - .with_secondary_label(operand.span(), "this operand cannot be immediate") - .emit(); - None - } - }; - imm.map(|imm| Instruction::UnaryOpImm(UnaryOpImm { op, overflow, imm })) - } - InstType::Br { - opcode: op, - successor, - } => { - match is_valid_successor( - &successor, - span, - blocks_by_id, - visited_blocks, - values_by_id, - diagnostics, - ) { - Ok(next) => { - if let Some(next) = next { - blockq.push_back(next); - } - let args = crate::ValueList::from_iter( - successor.args.iter().map(|arg| arg.item), - &mut function.dfg.value_lists, - ); - Some(Instruction::Br(crate::Br { - op, - destination: successor.id, - args, - })) - } - Err(_) => None, - } - } - InstType::CondBr { - opcode: op, - cond, - then_dest, - else_dest, - } => { - let mut is_valid = is_valid_value_reference(&cond, span, values_by_id, diagnostics); - - match is_valid_successor( - &then_dest, - span, - blocks_by_id, - visited_blocks, - values_by_id, - diagnostics, - ) { - Ok(next) => { - if let Some(next) = next { - blockq.push_back(next); - } - } - Err(_) => { - is_valid = false; - } - } - - match is_valid_successor( - &else_dest, - span, - blocks_by_id, - visited_blocks, - values_by_id, - diagnostics, - ) { - Ok(next) => { - if let Some(next) = next { - blockq.push_back(next); - } - } - Err(_) => { - is_valid = false; - } - } - - if is_valid { - let then_args = crate::ValueList::from_iter( - then_dest.args.iter().map(|arg| arg.item), - &mut function.dfg.value_lists, - ); - let else_args = crate::ValueList::from_iter( - else_dest.args.iter().map(|arg| arg.item), - &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), - })) - } else { - None - } - } - InstType::Switch { - opcode: op, - input, - successors, - fallback, - } => { - let mut is_valid = is_valid_value_reference(&input, span, values_by_id, diagnostics); - - let mut used_discriminants = FxHashSet::>::default(); - let mut arms = Vec::with_capacity(successors.len()); - for arm in successors.into_iter() { - let arm_span = arm.span(); - let discriminant = arm.item.0; - if !used_discriminants.insert(Span::new(arm_span, discriminant)) { - let prev = used_discriminants - .get(&Span::new(SourceSpan::UNKNOWN, discriminant)) - .unwrap() - .span(); - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label( - arm_span, - "the discriminant for this switch case has already been used", - ) - .with_secondary_label(prev, "previously used here") - .emit(); - is_valid = false; - } - let successor = arm.item.1; - arms.push((discriminant, successor.id)); - match is_valid_successor( - &successor, - span, - blocks_by_id, - visited_blocks, - values_by_id, - diagnostics, - ) { - Ok(next) => { - if let Some(next) = next { - blockq.push_back(next); - } - } - Err(_) => { - is_valid = false; - } - } - } - match is_valid_successor( - &fallback, - span, - blocks_by_id, - visited_blocks, - values_by_id, - diagnostics, - ) { - Ok(next) => { - if let Some(next) = next { - blockq.push_back(next); - } - } - Err(_) => { - is_valid = false; - } - } - - if is_valid { - Some(Instruction::Switch(crate::Switch { - op, - arg: input.item, - arms, - default: fallback.id, - })) - } else { - None - } - } - InstType::Ret { - opcode: op, - mut operands, - } => { - let expected_returns = function.signature.results.len(); - let actual_returns = operands.len(); - if actual_returns != expected_returns { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label(span, format!("the current function expects {expected_returns} values to be returned, but {actual_returns} are returned here")) - .emit(); - None - } else { - match actual_returns { - 0 => Some(Instruction::Ret(crate::Ret { - op, - args: Default::default(), - })), - 1 => match operands.pop().unwrap() { - Operand::Value(v) => { - if is_valid_value_reference(&v, span, values_by_id, diagnostics) { - let args = crate::ValueList::from_slice( - &[v.item], - &mut function.dfg.value_lists, - ); - Some(Instruction::Ret(crate::Ret { op, args })) - } else { - None - } - } - operand @ (Operand::Int(_) | Operand::BigInt(_)) => operand_to_immediate( - operand, - &function.signature.results[0].ty, - diagnostics, - ) - .map(|arg| Instruction::RetImm(crate::RetImm { op, arg })), - }, - _ => { - let mut is_valid = true; - 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); - is_valid &= - is_valid_value_reference(v, span, values_by_id, diagnostics); - } else { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid operand") - .with_primary_label( - operand.span(), - "expected an ssa value here, but got an immediate", - ) - .with_secondary_label(span, "occurs here") - .emit(); - is_valid = false; - } - } - if is_valid { - Some(Instruction::Ret(crate::Ret { op, args })) - } else { - None - } - } - } - } - } - InstType::Call { - opcode: op, - callee, - operands, - } => { - let mut is_valid = true; - if let Entry::Vacant(entry) = function.dfg.imports.entry(callee) { - if let Some(ef) = imports_by_id.get(&callee) { - entry.insert(ef.item.clone()); - } else { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid call instruction") - .with_primary_label(callee.span(), "this function is not imported in the current module, you must do so in order to reference it") - .with_secondary_label(span, "invalid callee for this instruction") - .emit(); - is_valid = false; - } - } - - is_valid &= - 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), - &mut function.dfg.value_lists, - ); - Some(Instruction::Call(crate::Call { op, callee, args })) - } else { - None - } - } - InstType::CallIndirect { .. } => { - unimplemented!("indirect calls are not implemented in the parser yet") - } - InstType::PrimOp { - opcode: op, - operands, - } => { - if operands.is_empty() { - Some(Instruction::PrimOp(PrimOp { - op, - args: Default::default(), - })) - } else { - let mut is_valid = true; - let mut imm = None; - let mut args = crate::ValueList::new(); - for (i, operand) in operands.iter().cloned().enumerate() { - let is_first = i == 0; - match operand { - Operand::Value(v) => { - is_valid &= - is_valid_value_reference(&v, span, values_by_id, diagnostics); - args.push(v.item, &mut function.dfg.value_lists); - } - operand @ (Operand::Int(_) | Operand::BigInt(_)) if is_first => { - imm = match op { - Opcode::AssertEq => operands[i + 1] - .as_value() - .and_then(|v| values_by_id.get(&v.item).map(|vd| vd.ty())) - .and_then(|ty| operand_to_immediate(operand, ty, diagnostics)), - Opcode::Store => { - operand_to_immediate(operand, &Type::U32, diagnostics) - } - opcode => { - unimplemented!("unsupported primop with immediate {opcode}") - } - }; - if imm.is_none() { - is_valid = false; - } - } - operand @ (Operand::Int(_) | Operand::BigInt(_)) => { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid immediate operand") - .with_primary_label(operand.span(), "expected an ssa value here, but got an immediate") - .with_secondary_label(span, "only the first argument of this instruction may be an immediate") - .emit(); - is_valid = false; - } - } - } - if is_valid { - if let Some(imm) = imm { - Some(Instruction::PrimOpImm(PrimOpImm { op, imm, args })) - } else { - Some(Instruction::PrimOp(crate::PrimOp { op, args })) - } - } else { - None - } - } - } - InstType::GlobalValue { - opcode: _op, - expr: _, - } => { - todo!() - } - }; - - // If the instruction data is invalid, we still need to handle the instruction results - // for subsequent instructions which may reference its results - if let Some(data) = data { - // Add instruction to DataFlowGraph - let node = crate::InstNode::new(id, block_data.id, Span::new(span, data)); - function.dfg.insts.append(id, node); - - // Add results to values_by_id map - for tv in results.into_iter() { - try_insert_result_value(tv.id, tv.span(), id, num, tv.ty, values_by_id, diagnostics); - } - - // Append instruction to block - let unsafe_ref = - unsafe { UnsafeRef::from_raw(function.dfg.insts.get_raw(id).unwrap().as_ptr()) }; - block_data.append(unsafe_ref); - - true - } else { - for tv in results.into_iter() { - try_insert_result_value(tv.id, tv.span(), id, num, tv.ty, values_by_id, diagnostics); - } - - false - } -} - -fn is_valid_successor( - successor: &Successor, - parent_span: SourceSpan, - blocks_by_id: &BlocksById, - visited: &FxHashSet, - values_by_id: &ValuesById, - diagnostics: &DiagnosticsHandler, -) -> Result, crate::Block> { - let is_visited = visited.contains(&successor.id); - let is_valid = is_valid_value_references( - successor.args.as_slice(), - parent_span, - values_by_id, - diagnostics, - ); - if blocks_by_id.contains_key(&successor.id) || is_visited { - if is_valid { - if is_visited { - Ok(None) - } else { - Ok(Some(successor.id)) - } - } else { - Err(successor.id) - } - } else { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label( - successor.span, - "invalid successor: the named block does not exist", - ) - .with_secondary_label(parent_span, "found in this instruction") - .emit(); - Err(successor.id) - } -} - -fn is_valid_value_references<'a, I: IntoIterator>>( - values: I, - parent_span: SourceSpan, - values_by_id: &ValuesById, - diagnostics: &DiagnosticsHandler, -) -> bool { - let mut is_valid = false; - let mut is_empty = true; - for value in values.into_iter() { - is_empty = false; - is_valid &= is_valid_value_reference(value, parent_span, values_by_id, diagnostics); - } - is_empty || is_valid -} - -fn is_valid_value_reference( - value: &Span, - parent_span: SourceSpan, - values_by_id: &ValuesById, - diagnostics: &DiagnosticsHandler, -) -> bool { - let is_valid = values_by_id.contains_key(&value.item); - if !is_valid { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid value operand") - .with_primary_label( - value.span(), - "this value is either undefined, or its definition does not dominate this use", - ) - .with_secondary_label(parent_span, "invalid use occurs in this instruction") - .emit(); - } - is_valid -} - -fn try_insert_param_value( - id: crate::Value, - span: SourceSpan, - block: crate::Block, - num: u16, - ty: Type, - values: &mut BTreeMap>, - diagnostics: &DiagnosticsHandler, -) -> Option { - use std::collections::btree_map::Entry; - - match values.entry(id) { - Entry::Vacant(entry) => { - let data = crate::ValueData::Param { - ty, - num, - block, - span, - }; - entry.insert(Span::new(span, data)); - Some(id) - } - Entry::Occupied(entry) => { - let prev = entry.get().span(); - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid block") - .with_primary_label(span, "the name of this block parameter is already in use") - .with_secondary_label(prev, "previously declared here") - .emit(); - None - } - } -} - -fn try_insert_result_value( - id: crate::Value, - span: SourceSpan, - inst: crate::Inst, - num: u16, - ty: Type, - values: &mut BTreeMap>, - diagnostics: &DiagnosticsHandler, -) -> Option { - use std::collections::btree_map::Entry; - - match values.entry(id) { - Entry::Vacant(entry) => { - let data = crate::ValueData::Inst { ty, num, inst }; - entry.insert(Span::new(span, data)); - Some(id) - } - Entry::Occupied(entry) => { - let prev = entry.get().span(); - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label(span, "a value may only be declared a single time") - .with_secondary_label(prev, "previously declared here") - .emit(); - None - } - } -} - -fn operand_to_immediate( - operand: Operand, - ty: &Type, - 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::Value(_) => panic!("cannot convert ssa values to immediate"), - } -} - -fn smallint_to_immediate( - span: SourceSpan, - i: isize, - ty: &Type, - diagnostics: &DiagnosticsHandler, -) -> Option { - match ty { - Type::I1 => Some(Immediate::I1(i != 0)), - Type::I8 => i8::try_from(i).ok().map(Immediate::I8), - Type::I16 => i16::try_from(i).ok().map(Immediate::I16), - Type::I32 => i32::try_from(i).ok().map(Immediate::I32), - Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 | Type::U256 if i < 0 => None, - Type::U8 => u8::try_from(i as usize).ok().map(Immediate::U8), - Type::U16 => u16::try_from(i as usize).ok().map(Immediate::U16), - Type::U32 => u32::try_from(i as usize).ok().map(Immediate::U32), - Type::I64 => Some(Immediate::I64(i as i64)), - Type::U64 => Some(Immediate::U64(i as u64)), - Type::I128 => Some(Immediate::I128(i as i128)), - Type::U128 | Type::U256 | Type::F64 => { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid immediate operand") - .with_primary_label( - span, - format!("immediates of type {ty} are not yet supported"), - ) - .emit(); - None - } - ty => { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid immediate operand") - .with_primary_label( - span, - format!("expected an immediate of type {ty}, but got an integer"), - ) - .emit(); - None - } - } -} - -fn bigint_to_immediate( - span: SourceSpan, - i: num_bigint::BigInt, - ty: &Type, - diagnostics: &DiagnosticsHandler, -) -> Option { - use num_traits::cast::ToPrimitive; - - let is_negative = i.sign() == num_bigint::Sign::Minus; - // NOTE: If we are calling this function, then `i` was too large to fit in an `isize`, so it must be a large integer type - let imm = match ty { - Type::U32 if !is_negative => i.to_u32().map(Immediate::U32), - Type::I64 => i.to_i64().map(Immediate::I64), - Type::U64 if !is_negative => i.to_u64().map(Immediate::U64), - Type::I128 => i.to_i128().map(Immediate::I128), - Type::U128 | Type::U256 | Type::F64 => { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid immediate operand") - .with_primary_label( - span, - format!("immediates of type {ty} are not yet supported"), - ) - .emit(); - return None; - } - ty if ty.is_integer() => { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid immediate operand") - .with_primary_label(span, format!("expected an immediate of type {ty}, but got {i}, which is out of range for that type")) - .emit(); - return None; - } - ty => { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid immediate operand") - .with_primary_label( - span, - format!("expected an immediate of type {ty}, but got an integer"), - ) - .emit(); - return None; - } - }; - if imm.is_none() { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid immediate operand") - .with_primary_label(span, format!("expected an immediate of type {ty}, but got {i}, which is out of range for that type")) - .emit(); - } - imm -} diff --git a/hir/src/parser/ast/functions.rs b/hir/src/parser/ast/functions.rs deleted file mode 100644 index c30cd78ef..000000000 --- a/hir/src/parser/ast/functions.rs +++ /dev/null @@ -1,125 +0,0 @@ -use core::fmt; - -use crate::{AttributeSet, Ident, Signature}; - -use super::*; - -/// Represents the declaration of a function in a [Module] -#[derive(Spanned)] -pub struct FunctionDeclaration { - #[span] - pub span: SourceSpan, - pub attrs: AttributeSet, - pub name: Ident, - pub signature: Signature, - pub blocks: Vec, -} -impl FunctionDeclaration { - pub fn new( - span: SourceSpan, - name: Ident, - signature: Signature, - blocks: Vec, - attrs: AttributeSet, - ) -> Self { - Self { - span, - attrs, - name, - signature, - blocks, - } - } - - /// Returns true if the entry block and signature match for this declaration - pub fn is_declaration_valid( - &self, - diagnostics: &miden_diagnostics::DiagnosticsHandler, - ) -> bool { - let entry_block = &self.blocks[0]; - if entry_block.params.len() != self.signature.arity() { - let num_expected = entry_block.params.len(); - let num_declared = self.signature.arity(); - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid function") - .with_primary_label( - entry_block.span, - "the parameter list of the entry block does not match the function signature", - ) - .with_secondary_label( - self.span, - format!("expected {num_expected} parameters, but got {num_declared}"), - ) - .emit(); - false - } else { - let mut is_valid = true; - for (expected, declared) in self.signature.params.iter().zip(entry_block.params.iter()) - { - let expected_ty = &expected.ty; - let declared_ty = &declared.ty; - if expected_ty != declared_ty { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid function") - .with_primary_label(entry_block.span, "the parameter list of the entry block does not match the function signature") - .with_secondary_label(declared.span, format!("expected a paramter of type {expected_ty}, but got {declared_ty}")) - .emit(); - is_valid = false; - } - } - - is_valid - } - } - - pub(super) fn populate_block_map( - &mut self, - diagnostics: &miden_diagnostics::DiagnosticsHandler, - ) -> Result { - use std::collections::hash_map::Entry; - - let mut blocks_by_id = BlocksById::default(); - let mut is_valid = true; - for block in core::mem::take(&mut self.blocks).into_iter() { - match blocks_by_id.entry(block.id) { - Entry::Vacant(entry) => { - entry.insert(block); - } - Entry::Occupied(entry) => { - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid block") - .with_primary_label( - block.span, - "a block with the same name has already been declared", - ) - .with_secondary_label(entry.get().span, "previously declared here") - .emit(); - is_valid = false; - continue; - } - } - } - - if is_valid { - Ok(blocks_by_id) - } else { - Err(blocks_by_id) - } - } -} -impl fmt::Debug for FunctionDeclaration { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("FunctionDeclaration") - .field("name", &self.name.as_symbol()) - .field("signature", &self.signature) - .field("blocks", &self.blocks) - .finish() - } -} -impl PartialEq for FunctionDeclaration { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.signature == other.signature && self.blocks == other.blocks - } -} diff --git a/hir/src/parser/ast/globals.rs b/hir/src/parser/ast/globals.rs deleted file mode 100644 index 0b05df5ca..000000000 --- a/hir/src/parser/ast/globals.rs +++ /dev/null @@ -1,85 +0,0 @@ -use core::fmt; - -use miden_diagnostics::{SourceSpan, Spanned}; - -use crate::{ConstantData, Ident, Linkage, Type}; - -/// This represents the declaration of a global variable -#[derive(Spanned)] -pub struct GlobalVarDeclaration { - #[span] - pub span: SourceSpan, - pub id: crate::GlobalVariable, - pub name: Ident, - pub ty: Type, - pub linkage: Linkage, - pub init: Option, -} -impl GlobalVarDeclaration { - pub fn new( - span: SourceSpan, - id: crate::GlobalVariable, - name: Ident, - ty: Type, - linkage: Linkage, - init: Option, - ) -> Self { - Self { - span, - id, - name, - ty, - linkage, - init, - } - } -} -impl fmt::Debug for GlobalVarDeclaration { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use crate::display::DisplayOptional; - - f.debug_struct("GlobalVarDeclaration") - .field("id", &format_args!("{}", &self.id)) - .field("name", &self.name.as_symbol()) - .field("ty", &self.ty) - .field("linkage", &self.linkage) - .field("init", &DisplayOptional(self.init.as_ref())) - .finish() - } -} -impl PartialEq for GlobalVarDeclaration { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - && self.name == other.name - && self.ty == other.ty - && self.linkage == other.linkage - && self.init == other.init - } -} - -/// This represents the declaration of a constant -#[derive(Spanned)] -pub struct ConstantDeclaration { - #[span] - pub span: SourceSpan, - pub id: crate::Constant, - pub init: ConstantData, -} -impl ConstantDeclaration { - pub fn new(span: SourceSpan, id: crate::Constant, init: ConstantData) -> Self { - Self { span, id, init } - } -} -impl fmt::Debug for ConstantDeclaration { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ConstantDeclaration") - .field("id", &format_args!("{}", &self.id)) - .field("init", &format_args!("{}", &self.init)) - .finish() - } -} -impl PartialEq for ConstantDeclaration { - fn eq(&self, other: &Self) -> bool { - self.id == other.id && self.init == other.init - } -} diff --git a/hir/src/parser/ast/instruction.rs b/hir/src/parser/ast/instruction.rs deleted file mode 100644 index d88cf7f01..000000000 --- a/hir/src/parser/ast/instruction.rs +++ /dev/null @@ -1,212 +0,0 @@ -use core::fmt; - -use miden_diagnostics::Span; - -use crate::{FunctionIdent, Ident, Opcode, Overflow, Type}; - -use super::*; - -/// Represents a single instruction. -#[derive(Spanned)] -pub struct Inst { - #[span] - pub span: SourceSpan, - /// The specific type of instruction and its data - pub ty: InstType, - /// If the instruction produces outputs, this will contain them, otherwise it is empty - pub outputs: Vec, -} -impl Inst { - pub fn new(span: SourceSpan, ty: InstType, outputs: Vec) -> Self { - Self { span, ty, outputs } - } -} -impl fmt::Debug for Inst { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Inst") - .field("ty", &self.ty) - .field("outputs", &self.outputs) - .finish() - } -} -impl PartialEq for Inst { - fn eq(&self, other: &Self) -> bool { - self.ty == other.ty && self.outputs == other.outputs - } -} - -/// This represents the various types of instructions which we can parse -#[derive(Debug, PartialEq, Eq)] -pub enum InstType { - BinaryOp { - opcode: Opcode, - overflow: Option, - operands: [Operand; 2], - }, - UnaryOp { - opcode: Opcode, - overflow: Option, - operand: Operand, - }, - Br { - opcode: Opcode, - successor: Successor, - }, - CondBr { - opcode: Opcode, - cond: Span, - then_dest: Successor, - else_dest: Successor, - }, - Switch { - opcode: Opcode, - input: Span, - successors: Vec>, - fallback: Successor, - }, - Ret { - opcode: Opcode, - operands: Vec, - }, - Call { - opcode: Opcode, - callee: FunctionIdent, - operands: Vec>, - }, - CallIndirect { - opcode: Opcode, - operands: Vec, - }, - PrimOp { - opcode: Opcode, - operands: Vec, - }, - GlobalValue { - opcode: Opcode, - expr: GlobalValueExpr, - }, -} - -/// An operand is an argument to an instruction -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum Operand { - Value(Span), - /// A small integer type, e.g. u32 - Int(Span), - /// A large integer type, e.g. i128 or u256 - BigInt(Span), -} -impl Operand { - pub fn span(&self) -> SourceSpan { - match self { - Self::Value(ref spanned) => spanned.span(), - Self::Int(ref spanned) => spanned.span(), - Self::BigInt(ref spanned) => spanned.span(), - } - } - - pub fn as_value(&self) -> Option> { - match self { - Self::Value(v) => Some(*v), - _ => None, - } - } -} - -/// Represents a value/type pair where applicable in the AST -#[derive(Spanned)] -pub struct TypedValue { - #[span] - pub span: SourceSpan, - pub id: crate::Value, - pub ty: Type, -} -impl TypedValue { - pub fn new(span: SourceSpan, id: crate::Value, ty: Type) -> Self { - Self { span, id, ty } - } -} -impl fmt::Display for TypedValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.id) - } -} -impl fmt::Debug for TypedValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("TypedValue") - .field("id", &self.id) - .field("ty", &self.ty) - .finish() - } -} -impl Eq for TypedValue {} -impl PartialEq for TypedValue { - fn eq(&self, other: &Self) -> bool { - self.id == other.id && self.ty == other.ty - } -} - -/// This represents a branch destination and arguments -#[derive(Spanned)] -pub struct Successor { - #[span] - pub span: SourceSpan, - pub id: crate::Block, - pub args: Vec>, -} -impl fmt::Debug for Successor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Successor") - .field("id", &format_args!("{}", &self.id)) - .field( - "args", - &format_args!("{}", crate::display::DisplayValues::new(self.args.iter())), - ) - .finish() - } -} -impl Eq for Successor {} -impl PartialEq for Successor { - fn eq(&self, other: &Self) -> bool { - self.id == other.id && self.args == other.args - } -} - -/// This represents access or a relative operation to a global variable -#[derive(Debug, PartialEq, Eq)] -pub enum GlobalValueExpr { - Symbol { - symbol: Ident, - offset: i32, - span: SourceSpan, - }, - Load { - base: Box, - offset: i32, - ty: Option, - span: SourceSpan, - }, - IAddImm { - base: Box, - offset: i32, - ty: Type, - span: SourceSpan, - }, -} -impl GlobalValueExpr { - pub fn ty(&self) -> Option { - match self { - Self::Symbol { .. } => None, - Self::Load { ref ty, .. } => ty.clone(), - Self::IAddImm { ref base, .. } => base.ty(), - } - } - - pub fn span(&self) -> SourceSpan { - match self { - Self::Symbol { span, .. } | Self::Load { span, .. } | Self::IAddImm { span, .. } => { - *span - } - } - } -} diff --git a/hir/src/parser/ast/mod.rs b/hir/src/parser/ast/mod.rs deleted file mode 100644 index 9bdf59900..000000000 --- a/hir/src/parser/ast/mod.rs +++ /dev/null @@ -1,235 +0,0 @@ -mod block; -mod convert; -mod functions; -mod globals; -mod instruction; - -pub use self::block::*; -pub use self::convert::ConvertAstToHir; -pub use self::functions::*; -pub use self::globals::*; -pub use self::instruction::*; - -use std::{collections::BTreeMap, fmt}; - -use miden_diagnostics::{DiagnosticsHandler, Severity, SourceSpan, Span, Spanned}; -use rustc_hash::FxHashMap; - -use crate::{ExternalFunction, FunctionIdent, Ident}; - -/// This represents the parsed contents of a single Miden IR module -#[derive(Spanned)] -pub struct Module { - #[span] - pub span: SourceSpan, - pub name: Ident, - pub constants: Vec, - pub global_vars: Vec, - pub functions: Vec, - pub externals: Vec>, - pub is_kernel: bool, -} -impl fmt::Debug for Module { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Module") - .field("name", &self.name.as_symbol()) - .field("constants", &self.constants) - .field("global_vars", &self.global_vars) - .field("functions", &self.functions) - .field("externals", &self.externals) - .field("is_kernel", &self.is_kernel) - .finish() - } -} -impl midenc_session::Emit for Module { - fn name(&self) -> Option { - Some(self.name.as_symbol()) - } - fn output_type(&self) -> midenc_session::OutputType { - midenc_session::OutputType::Ast - } - fn write_to(&self, mut writer: W) -> std::io::Result<()> { - writer.write_fmt(format_args!("{:#?}", self)) - } -} - -type ConstantsById = FxHashMap>; -type GlobalVariablesById = FxHashMap>; -type ImportsById = FxHashMap>; -type BlocksById = FxHashMap; -type ValuesById = BTreeMap>; - -impl Module { - pub fn new(span: SourceSpan, name: Ident, is_kernel: bool, forms: Vec
) -> Self { - let mut module = Self { - span, - name, - constants: vec![], - functions: vec![], - externals: vec![], - global_vars: vec![], - is_kernel, - }; - for form in forms.into_iter() { - match form { - Form::Constant(constant) => { - module.constants.push(constant); - } - Form::Global(global) => { - module.global_vars.push(global); - } - Form::Function(function) => { - module.functions.push(function); - } - Form::ExternalFunction(external) => { - module.externals.push(external); - } - } - } - module - } - - fn take_and_validate_constants( - &mut self, - diagnostics: &DiagnosticsHandler, - ) -> (ConstantsById, bool) { - use std::collections::hash_map::Entry; - - let mut constants_by_id = ConstantsById::default(); - let constants = core::mem::take(&mut self.constants); - let mut is_valid = true; - for constant in constants.into_iter() { - match constants_by_id.entry(constant.id) { - Entry::Vacant(entry) => { - entry.insert(Span::new(constant.span, constant.init)); - } - Entry::Occupied(entry) => { - let prev = entry.get().span(); - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid constant declaration") - .with_primary_label( - constant.span, - "a constant with this identifier has already been declared", - ) - .with_secondary_label(prev, "previously declared here") - .emit(); - is_valid = false; - } - } - } - - (constants_by_id, is_valid) - } - - fn take_and_validate_globals( - &mut self, - constants_by_id: &ConstantsById, - diagnostics: &DiagnosticsHandler, - ) -> (GlobalVariablesById, bool) { - use std::collections::hash_map::Entry; - - let mut globals_by_id = GlobalVariablesById::default(); - let global_vars = core::mem::take(&mut self.global_vars); - let mut is_valid = true; - for global in global_vars.into_iter() { - match globals_by_id.entry(global.id) { - Entry::Vacant(entry) => { - if let Some(id) = global.init { - if !constants_by_id.contains_key(&id) { - let id = id.as_u32(); - diagnostics.diagnostic(Severity::Error) - .with_message("invalid global variable declaration") - .with_primary_label(global.span, format!("invalid initializer: no constant named '{id}' in this module")) - .emit(); - is_valid = false; - } - } - let gv = crate::GlobalVariableData::new( - global.id, - global.name, - global.ty, - global.linkage, - global.init, - ); - entry.insert(Span::new(global.span, gv)); - } - Entry::Occupied(entry) => { - let prev = entry.get().span(); - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid global variable declaration") - .with_primary_label( - global.span, - "a global variable with the same id has already been declared", - ) - .with_secondary_label(prev, "previously declared here") - .emit(); - is_valid = false; - } - } - } - - (globals_by_id, is_valid) - } - - fn take_and_validate_imports( - &mut self, - diagnostics: &DiagnosticsHandler, - ) -> (ImportsById, bool) { - use std::collections::hash_map::Entry; - - let mut imports_by_id = ImportsById::default(); - let mut is_valid = true; - for external in core::mem::take(&mut self.externals).into_iter() { - if external.id.module == self.name { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid external function declaration") - .with_primary_label(external.span(), "external function declarations may not reference functions in the current module") - .emit(); - is_valid = false; - continue; - } - - match imports_by_id.entry(external.id) { - Entry::Vacant(entry) => { - entry.insert(external); - } - Entry::Occupied(entry) => { - let prev = entry.get().span(); - diagnostics - .diagnostic(Severity::Error) - .with_message("invalid external function declaration") - .with_primary_label( - external.span(), - "an external function with the same name has already been declared", - ) - .with_secondary_label(prev, "previously declared here") - .emit(); - is_valid = false; - } - } - } - - (imports_by_id, is_valid) - } -} - -impl PartialEq for Module { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - && self.is_kernel == other.is_kernel - && self.global_vars == other.global_vars - && self.functions == other.functions - && self.externals == other.externals - } -} - -/// This represents one of the top-level forms which a [Module] can contain -#[derive(Debug)] -pub enum Form { - Constant(ConstantDeclaration), - Global(GlobalVarDeclaration), - Function(FunctionDeclaration), - ExternalFunction(Span), -} diff --git a/hir/src/parser/error.rs b/hir/src/parser/error.rs deleted file mode 100644 index 3fc9e2ef5..000000000 --- a/hir/src/parser/error.rs +++ /dev/null @@ -1,160 +0,0 @@ -use miden_diagnostics::{Diagnostic, Label, SourceIndex, SourceSpan, ToDiagnostic}; - -use super::lexer::{LexicalError, Token}; - -#[derive(Debug, thiserror::Error)] -pub enum ParseError { - #[error(transparent)] - Lexer(#[from] LexicalError), - #[error("error reading {path:?}: {source}")] - FileError { - source: std::io::Error, - path: std::path::PathBuf, - }, - #[error("invalid token")] - InvalidToken(SourceIndex), - #[error("unexpected end of file")] - UnexpectedEof { - at: SourceIndex, - expected: Vec, - }, - #[error("unrecognized token '{token}'")] - UnrecognizedToken { - span: SourceSpan, - token: Token, - expected: Vec, - }, - #[error("extraneous token '{token}'")] - ExtraToken { span: SourceSpan, token: Token }, - #[error("expected valid u32 value, got '{value}'")] - InvalidU32 { span: SourceSpan, value: isize }, - #[error("expected valid offset value, got '{value}'")] - InvalidOffset { span: SourceSpan, value: isize }, - #[error("parsing succeeded, but validation failed, see diagnostics for details")] - InvalidModule, - #[error("parsing failed, see diagnostics for details")] - Failed, -} -impl Eq for ParseError {} -impl PartialEq for ParseError { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Lexer(l), Self::Lexer(r)) => l == r, - (Self::FileError { .. }, Self::FileError { .. }) => true, - (Self::InvalidToken(_), Self::InvalidToken(_)) => true, - ( - Self::UnexpectedEof { - expected: ref l, .. - }, - Self::UnexpectedEof { - expected: ref r, .. - }, - ) => l == r, - ( - Self::UnrecognizedToken { - token: lt, - expected: ref l, - .. - }, - Self::UnrecognizedToken { - token: rt, - expected: ref r, - .. - }, - ) => 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::InvalidU32 { value: l, .. }, Self::InvalidU32 { value: r, .. }) => l == r, - (Self::InvalidOffset { value: l, .. }, Self::InvalidOffset { value: r, .. }) => l == r, - _ => false, - } - } -} -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::UnrecognizedEof { - location: at, - expected, - } => Self::UnexpectedEof { at, expected }, - LError::UnrecognizedToken { - token: (l, token, r), - expected, - } => Self::UnrecognizedToken { - span: SourceSpan::new(l, r), - token, - expected, - }, - LError::ExtraToken { - token: (l, token, r), - } => Self::ExtraToken { - span: SourceSpan::new(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 value u32 value") - .with_labels(vec![Label::primary(span.source_id(), span)]), - Self::InvalidOffset { span, .. } => Diagnostic::error() - .with_message("expected value offset value") - .with_labels(vec![Label::primary(span.source_id(), span)]), - err => Diagnostic::error().with_message(err.to_string()), - } - } -} diff --git a/hir/src/parser/grammar.lalrpop b/hir/src/parser/grammar.lalrpop deleted file mode 100644 index 6744563a0..000000000 --- a/hir/src/parser/grammar.lalrpop +++ /dev/null @@ -1,673 +0,0 @@ -use std::sync::Arc; - -use miden_diagnostics::{CodeMap, DiagnosticsHandler, Span, Spanned, SourceSpan, Severity}; - -use crate::{AbiParam, AddressSpace, ArgumentExtension, ArgumentPurpose}; -use crate::{CallConv, ConstantData, ExternalFunction, FunctionIdent}; -use crate::{Ident, Linkage, Opcode, Overflow, Signature, StructType, symbols, Symbol, Type}; -use crate::{AttributeSet, Attribute, AttributeValue}; -use crate::parser::{ - ast::*, - lexer::Token, - ParseError -}; - -grammar(diagnostics: &DiagnosticsHandler, codemap: &Arc, next_var: &mut usize); - -// MACROS -// ================================================================================================ - -// Comma-delimited with at least one element -#[inline] -Comma: Vec = { - ",")*> => { - let mut v = v; - v.push(e); - v - } -}; - -// Comma-delimited, possibly empty, possibly with a trailing comma -#[inline] -CommaOpt: Vec = { - ",")*> => { - let mut v = v; - v.extend(e); - v - } -}; - -// AST NODE -// ================================================================================================ - -pub Module: Module = { - "kernel" => { - Module::new(span!(l, r), name, /*is_kernel=*/true, forms) - }, - "module" => { - Module::new(span!(l, r), name, /*is_kernel=*/false, forms) - }, -} - -Form: Form = { - ConstantDeclaration => Form::Constant(<>), - GlobalVarDeclaration => Form::Global(<>), - FunctionDeclaration => Form::Function(<>), - ExternalFunctionDeclaration => Form::ExternalFunction(<>), -} - -// GLOBALS -// ================================================================================================ - -ConstantDeclaration: ConstantDeclaration = { - "const" "$" "=" ";" - => ConstantDeclaration::new(span!(l, r), crate::Constant::from_u32(id), init), -} - -GlobalVarDeclaration: GlobalVarDeclaration = { - "global" "@" ":" )?> "{" "id" "=" "}" ";" - => GlobalVarDeclaration::new(span!(l, r), crate::GlobalVariable::from_u32(id), name, ty, linkage, init.map(crate::Constant::from_u32)), -} - -Linkage: Linkage = { - "internal" => Linkage::Internal, - "odr" => Linkage::Odr, - "external" => Linkage::Internal, -} - -// TYPES -// ============================================================================================== - -Type: Type = { - "(" ")" => Type::Unit, - "!" => Type::Never, - "i1" => Type::I1, - "i8" => Type::I8, - "u8" => Type::U8, - "i16" => Type::I16, - "u16" => Type::U16, - "i32" => Type::I32, - "u32" => Type::U32, - "i64" => Type::I64, - "u64" => Type::U64, - "i128" => Type::I128, - "u128" => Type::U128, - "u256" => Type::U256, - "f64" => Type::F64, - "felt" => Type::Felt, - "*" "mut" => Type::Ptr(Box::new(pointee)), - "&" "mut" => Type::NativePtr(Box::new(pointee), AddressSpace::Unknown), - StructType, - ArrayType, -} - -StructType: Type = { - "{" > "}" => Type::Struct(StructType::new(fields)), -} - -ArrayType: Type = { - "[" ";" "]" => Type::Array(Box::new(element), usize::try_from(len).unwrap()), -} - -// ATTRIBUTES -// ============================================================================================== - -Attribute: Attribute = { - "#" "[" "]" => Attribute { name: name.as_symbol(), value: AttributeValue::Unit }, - - "#" "[" "(" ")" => Attribute { name: name.as_symbol(), value }, -} - -AttributeValue: AttributeValue = { - => match name.as_symbol() { - symbols::True => AttributeValue::Bool(true), - symbols::False => AttributeValue::Bool(false), - symbol => AttributeValue::String(symbol), - }, - - => AttributeValue::Int(n), - - => AttributeValue::Unit, -} - -// FUNCTIONS -// ============================================================================================== - -ExternalFunctionDeclaration: Span = { - "extern" "fn" "(" > ")" " >)?> ";" => { - let span = span!(l, r); - let cc = cc.unwrap_or(CallConv::SystemV); - let results = results.unwrap_or_default(); - Span::new(span, ExternalFunction { - id, - signature: Signature { - params, - results, - cc, - linkage: Linkage::External, - } - }) - }, -} - -FunctionDeclaration: FunctionDeclaration = { - "fn" "(" > ")" " >)?> "{" "}" => { - let cc = cc.unwrap_or(CallConv::SystemV); - let results = results.unwrap_or_default(); - let signature = Signature { - params, - results, - cc, - linkage, - }; - FunctionDeclaration::new(span!(l, r), name, signature, blocks, AttributeSet::from_iter(attrs)) - }, -} - -FunctionVisibility: Linkage = { - "pub" => Linkage::External, - => Linkage::Internal, -} - -CallConv: CallConv = { - "cc" "(" "fast" ")" => CallConv::Fast, - "cc" "(" "kernel" ")" => CallConv::Kernel, -} - -AbiParam: AbiParam = { - => { - AbiParam { - ty, - purpose, - extension: extension.unwrap_or(ArgumentExtension::None), - } - } -} - -ArgumentPurpose: ArgumentPurpose = { - "sret" => ArgumentPurpose::StructReturn, - => ArgumentPurpose::Default, -} - -ArgumentExtension: ArgumentExtension = { - "zext" => ArgumentExtension::Zext, - "sext" => ArgumentExtension::Sext, -} - - -// BLOCKS -// ================================================================================================ - -Block: Block = { - > ")")?> ":" => { - Block::new(span!(l, r), id, args.unwrap_or_default(), insts) - } -} - -BlockArg: TypedValue = { - ":" => TypedValue::new(span!(l, r), id, ty), -} - -// INSTRUCTIONS -// ================================================================================================ - -Inst: Inst = { - "=" ":" ";" => { - let (opcode, overflow) = op_and_overflow; - Inst::new(span!(l, r), InstType::UnaryOp { opcode, overflow, operand }, vec![TypedValue::new(value.span(), value.item, value_ty)]) - }, - - "=" "," ":" ";" => { - let (opcode, overflow) = op_and_overflow; - Inst::new(span!(l, r), InstType::BinaryOp { opcode, overflow, operands: [lhs, rhs] }, vec![TypedValue::new(value.span(), value.item, value_ty)]) - }, - - > "=" ":" "," ";" =>? { - let num_results = values.len(); - if num_results != 2 { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label(span!(l, r), format!("expected this instruction to have two results, but got {num_results}")) - .emit(); - Err(ParseError::Failed.into()) - } else { - let mut values = values; - let value = values.pop().unwrap(); - let value = TypedValue::new(value.span(), value.item, value_ty); - let overflowed = values.pop().unwrap(); - let overflowed = TypedValue::new(overflowed.span(), overflowed.item, overflowed_ty); - Ok(Inst::new(span!(l, r), InstType::UnaryOp { opcode, overflow: Some(Overflow::Overflowing), operand }, vec![overflowed, value])) - } - }, - - > "=" "," ":" "," ";" =>? { - let num_results = values.len(); - if num_results != 2 { - diagnostics.diagnostic(Severity::Error) - .with_message("invalid instruction") - .with_primary_label(span!(l, r), format!("expected this instruction to have two results, but got {num_results}")) - .emit(); - Err(ParseError::Failed.into()) - } else { - let mut values = values; - let value = values.pop().unwrap(); - let value = TypedValue::new(value.span(), value.item, value_ty); - let overflowed = values.pop().unwrap(); - let overflowed = TypedValue::new(overflowed.span(), overflowed.item, overflowed_ty); - Ok(Inst::new(span!(l, r), InstType::BinaryOp { opcode, overflow: Some(Overflow::Overflowing), operands: [lhs, rhs] }, vec![overflowed, value])) - } - }, - - > "=" > ":" > ";" => { - assert_eq!(values.len(), types.len()); - let outputs = values.into_iter().zip(types.into_iter()).map(|(v, ty)| TypedValue::new(v.span(), v.item, ty)).collect(); - Inst::new(span!(l, r), InstType::PrimOp { opcode, operands }, outputs) - }, - - > "=" "(" > ")" >)?> ";" => { - let types = types.unwrap_or_default(); - assert_eq!(values.len(), types.len()); - let outputs = values.into_iter().zip(types.into_iter()).map(|(v, ty)| TypedValue::new(v.span(), v.item, ty)).collect(); - Inst::new(span!(l, r), InstType::Call { opcode, callee, operands }, outputs) - }, - - "=" ":" ";" => { - Inst::new(span!(l, r), InstType::GlobalValue { opcode: Opcode::GlobalValue, expr }, vec![TypedValue::new(value.span(), value.item, value_ty)]) - }, - - "unreachable" ";" => { - Inst::new(span!(l, r), InstType::PrimOp { opcode: Opcode::Unreachable, operands: vec![] }, vec![]) - }, - - "ret" ?> ";" => { - Inst::new(span!(l, r), InstType::Ret { opcode: Opcode::Ret, operands: operands.unwrap_or_default() }, vec![]) - }, - - "br" ";" => { - Inst::new(span!(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![]) - }, - - "switch" "{" > "}" =>? { - let mut arms = arms; - let fallback = match arms.pop().unwrap() { - (None, successor, _) => successor, - (Some(_), _, _) => panic!("invalid switch: default arm is required"), - }; - let mut successors = vec![]; - for arm in arms.into_iter() { - match arm { - (Some(id), successor, span) => successors.push(Span::new(span, (id, successor))), - (None, _, _) => panic!("invalid switch: only one default arm is allowed"), - } - } - Ok(Inst::new(span!(l, r), InstType::Switch { opcode: Opcode::Switch, input, successors, fallback }, vec![])) - }, -} - -Successor: Successor = { - > ")")?> => Successor { span: span!(l, r), id, args: args.unwrap_or_default() }, -} - -SwitchArm: (Option, Successor, SourceSpan) = { - "=>" => (Some(value), successor, span!(l, r)), - "_" "=>" => (None, successor, span!(l, r)), -} - -CallOpcode: Opcode = { - "call" => Opcode::Call, - "syscall" => Opcode::Syscall, -} - -UnaryOpcode: (Opcode, Option) = { - "const.i1" => (Opcode::ImmI1, None), - "const.u8" => (Opcode::ImmU8, None), - "const.i8" => (Opcode::ImmU8, None), - "const.u16" => (Opcode::ImmU16, None), - "const.i16" => (Opcode::ImmI16, None), - "const.u32" => (Opcode::ImmU32, None), - "const.i32" => (Opcode::ImmI32, None), - "const.u64" => (Opcode::ImmU64, None), - "const.i64" => (Opcode::ImmI64, None), - "const.felt" => (Opcode::ImmFelt, None), - "neg" => (Opcode::Neg, None), - "inv" => (Opcode::Inv, None), - "incr.unchecked" => (Opcode::Incr, Some(Overflow::Unchecked)), - "incr.checked" => (Opcode::Incr, Some(Overflow::Checked)), - "incr.wrapping" => (Opcode::Incr, Some(Overflow::Wrapping)), - "pow2" => (Opcode::Pow2, None), - "not" => (Opcode::Not, None), - "bnot" => (Opcode::Bnot, None), - "popcnt" => (Opcode::Popcnt, None), - "ptrtoint" => (Opcode::PtrToInt, None), - "inttoptr" => (Opcode::IntToPtr, None), - "cast" => (Opcode::Cast, None), - "trunc" => (Opcode::Trunc, None), - "zext" => (Opcode::Zext, None), - "sext" => (Opcode::Sext, None), - "is_odd" => (Opcode::IsOdd, None), -} - -OverflowingUnaryOpcode: Opcode = { - "incr.overflowing" => Opcode::Incr, -} - -BinaryOpcode: (Opcode, Option) = { - "eq" => (Opcode::Eq, None), - "neq" => (Opcode::Neq, None), - "gt" => (Opcode::Gt, None), - "gte" => (Opcode::Gte, None), - "lt" => (Opcode::Lt, None), - "lte" => (Opcode::Lte, None), - "min" => (Opcode::Min, None), - "max" => (Opcode::Max, None), - "add.unchecked" => (Opcode::Add, Some(Overflow::Unchecked)), - "add.checked" => (Opcode::Add, Some(Overflow::Checked)), - "add.wrapping" => (Opcode::Add, Some(Overflow::Wrapping)), - "sub.unchecked" => (Opcode::Sub, Some(Overflow::Unchecked)), - "sub.checked" => (Opcode::Sub, Some(Overflow::Checked)), - "sub.wrapping" => (Opcode::Sub, Some(Overflow::Wrapping)), - "mul.unchecked" => (Opcode::Mul, Some(Overflow::Unchecked)), - "mul.checked" => (Opcode::Mul, Some(Overflow::Checked)), - "mul.wrapping" => (Opcode::Mul, Some(Overflow::Wrapping)), - "div.unchecked" => (Opcode::Div, Some(Overflow::Unchecked)), - "div.checked" => (Opcode::Div, Some(Overflow::Checked)), - "mod.unchecked" => (Opcode::Mod, Some(Overflow::Unchecked)), - "mod.checked" => (Opcode::Mod, Some(Overflow::Checked)), - "divmod.unchecked" => (Opcode::DivMod, Some(Overflow::Unchecked)), - "divmod.checked" => (Opcode::DivMod, Some(Overflow::Checked)), - "exp" => (Opcode::Exp, None), - "and" => (Opcode::And, None), - "band" => (Opcode::Band, None), - "or" => (Opcode::Or, None), - "bor" => (Opcode::Bor, None), - "xor" => (Opcode::Xor, None), - "bxor" => (Opcode::Bxor, None), - "shl.unchecked" => (Opcode::Shl, Some(Overflow::Unchecked)), - "shl.checked" => (Opcode::Shl, Some(Overflow::Checked)), - "shl.wrapping" => (Opcode::Shl, Some(Overflow::Wrapping)), - "shr.unchecked" => (Opcode::Shr, Some(Overflow::Unchecked)), - "shr.checked" => (Opcode::Shr, Some(Overflow::Checked)), - "shr.wrapping" => (Opcode::Shr, Some(Overflow::Wrapping)), - "rotl" => (Opcode::Rotl, None), - "rotr" => (Opcode::Rotr, None), -} - -OverflowingBinaryOpcode: Opcode = { - "add.overflowing" => Opcode::Add, - "sub.overflowing" => Opcode::Sub, - "mul.overflowing" => Opcode::Mul, - "shl.overflowing" => Opcode::Shl, - "shr.overflowing" => Opcode::Shr, -} - -PrimOpOpcode: Opcode = { - "assert" => Opcode::Assert, - "assertz" => Opcode::Assertz, - "assert.eq" => Opcode::AssertEq, - "alloca" => Opcode::Alloca, - "store" => Opcode::Store, - "load" => Opcode::Load, - "memcpy" => Opcode::MemCpy, - "memory.grow" => Opcode::MemGrow, - "select" => Opcode::Select, -} - -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)), -} - -GlobalValueExpr: GlobalValueExpr = { - "global.symbol" "@" => GlobalValueExpr::Symbol { symbol, offset: offset.unwrap_or(0), span: span!(l, r) }, - - "global.load" "(" ")" )?> => { - GlobalValueExpr::Load { base: Box::new(base), offset, ty, span: span!(l, r) } - }, - - "global.load" => { - GlobalValueExpr::Load { base: Box::new(base), offset: 0, ty: None, span: span!(l, r) } - }, - - "global.iadd" "." "." => { - GlobalValueExpr::IAddImm { base: Box::new(base), offset, ty, span: span!(l, r) } - } -} - -NestedGlobalValueExpr: GlobalValueExpr = { - "@" => GlobalValueExpr::Symbol { symbol, offset: offset.unwrap_or(0), span: span!(l, r) }, - - "*" "(" ")" )?> => { - GlobalValueExpr::Load { base: Box::new(base), offset, ty, span: span!(l, r) } - }, - - "*" => { - GlobalValueExpr::Load { base: Box::new(base), offset: 0, ty: None, span: span!(l, r) } - }, - - "iadd" "." "." => { - GlobalValueExpr::IAddImm { base: Box::new(base), offset, ty, span: span!(l, r) } - } -} - -// VALUES AND IDENTIFIERS -// ================================================================================================ - -HexString: ConstantData = { - data, -} - -Int: isize = { - int, -} - -Offset: i32 = { - =>? { - match i32::try_from(i) { - Ok(v) => Ok(v), - Err(_) => Err(ParseError::InvalidOffset { span: span!(l, r), value: i }.into()), - } - } -} - -Index: u32 = { - =>? { - match u32::try_from(i) { - Ok(v) => Ok(v), - Err(_) => Err(ParseError::InvalidU32 { span: span!(l, r), value: i }.into()), - } - } -} - -Ident: Ident = { - => Ident::new(id, span!(l, r)), -} - -FunctionIdent: FunctionIdent = { - => { - let offset = id.0.as_str().as_bytes().len(); - let module_span = SourceSpan::new(l, l + offset); - let function_span = SourceSpan::new(l + offset + 2, r); - FunctionIdent { module: Ident::new(id.0, module_span), function: Ident::new(id.1, function_span) } - } -} - -SpannedValueId: Span = { - => Span::new(span!(l, r), v), -} - -ValueId: crate::Value = { - value_id, -} - -BlockId: crate::Block = { - block_id, -} - - -// LEXER -// ================================================================================================ - -extern { - type Error = ParseError; - type Location = miden_diagnostics::SourceIndex; - - enum Token { - ident => Token::Ident(), - function_ident => Token::FunctionIdent(<(Symbol, Symbol)>), - int => Token::Int(), - bigint => Token::BigInt(), - data => Token::Hex(), - value_id => Token::Value(), - block_id => Token::Block(), - "kernel" => Token::Kernel, - "module" => Token::Module, - "internal" => Token::Internal, - "odr" => Token::Odr, - "external" => Token::External, - "extern" => Token::Extern, - "pub" => Token::Pub, - "fn" => Token::Fn, - "cc" => Token::Cc, - "fast" => Token::Fast, - "sret" => Token::Sret, - "zext" => Token::Zext, - "sext" => Token::Sext, - "trunc" => Token::Trunc, - "ret" => Token::Ret, - "call" => Token::Call, - "syscall" => Token::Syscall, - "br" => Token::Br, - "condbr" => Token::CondBr, - "switch" => Token::Switch, - "test" => Token::Test, - "load" => Token::Load, - "memcpy" => Token::MemCpy, - "asm" => Token::Asm, - "memory.grow" => Token::MemoryGrow, - "add.unchecked" => Token::AddUnchecked, - "add.checked" => Token::AddChecked, - "add.overflowing" => Token::AddOverflowing, - "add.wrapping" => Token::AddWrapping, - "sub.unchecked" => Token::SubUnchecked, - "sub.checked" => Token::SubChecked, - "sub.overflowing" => Token::SubOverflowing, - "sub.wrapping" => Token::SubWrapping, - "mul.unchecked" => Token::MulUnchecked, - "mul.checked" => Token::MulChecked, - "mul.overflowing" => Token::MulOverflowing, - "mul.wrapping" => Token::MulWrapping, - "div.unchecked" => Token::DivUnchecked, - "div.checked" => Token::DivChecked, - "mod.unchecked" => Token::ModUnchecked, - "mod.checked" => Token::ModChecked, - "divmod.unchecked" => Token::DivModUnchecked, - "divmod.checked" => Token::DivModChecked, - "min" => Token::Min, - "max" => Token::Max, - "exp" => Token::Exp, - "and" => Token::And, - "band" => Token::BAnd, - "or" => Token::Or, - "bor" => Token::BOr, - "xor" => Token::Xor, - "bxor" => Token::BXor, - "shl.unchecked" => Token::ShlUnchecked, - "shl.checked" => Token::ShlChecked, - "shl.wrapping" => Token::ShlWrapping, - "shl.overflowing" => Token::ShlOverflowing, - "shr.unchecked" => Token::ShrUnchecked, - "shr.checked" => Token::ShrChecked, - "shr.wrapping" => Token::ShrWrapping, - "shr.overflowing" => Token::ShrOverflowing, - "rotl" => Token::Rotl, - "rotr" => Token::Rotr, - "eq" => Token::Eq, - "neq" => Token::Neq, - "gt" => Token::Gt, - "gte" => Token::Gte, - "lt" => Token::Lt, - "lte" => Token::Lte, - "store" => Token::Store, - "inv" => Token::Inv, - "incr.unchecked" => Token::IncrUnchecked, - "incr.checked" => Token::IncrChecked, - "incr.wrapping" => Token::IncrWrapping, - "incr.overflowing" => Token::IncrOverflowing, - "pow2" => Token::Pow2, - "not" => Token::Not, - "bnot" => Token::BNot, - "popcnt" => Token::PopCnt, - "is_odd" => Token::IsOdd, - "cast" => Token::Cast, - "ptrtoint" => Token::PtrToInt, - "inttoptr" => Token::IntToPtr, - "neg" => Token::Neg, - "const.i1" => Token::ConstI1, - "const.i8" => Token::ConstI8, - "const.u8" => Token::ConstU8, - "const.i16" => Token::ConstI16, - "const.u16" => Token::ConstU16, - "const.i32" => Token::ConstI32, - "const.u32" => Token::ConstU32, - "const.i64" => Token::ConstI64, - "const.u64" => Token::ConstU64, - "const.felt" => Token::ConstFelt, - "select" => Token::Select, - "assert" => Token::Assert, - "assertz" => Token::Assertz, - "assert.eq" => Token::AssertEq, - "alloca" => Token::Alloca, - "unreachable" => Token::Unreachable, - "i1" => Token::I1, - "i8" => Token::I8, - "u8" => Token::U8, - "i16" => Token::I16, - "u16" => Token::U16, - "i32" => Token::I32, - "u32" => Token::U32, - "i64" => Token::I64, - "u64" => Token::U64, - "i128" => Token::I128, - "u128" => Token::U128, - "u256" => Token::U256, - "f64" => Token::F64, - "felt" => Token::Felt, - "mut" => Token::Mut, - "as" => Token::As, - "id" => Token::Id, - "const" => Token::Const, - "global" => Token::Global, - "global.symbol" => Token::GlobalSymbol, - "global.load" => Token::GlobalLoad, - "global.iadd" => Token::GlobalIAdd, - "symbol" => Token::Symbol, - "iadd" => Token::IAdd, - "=" => Token::Equal, - "=>" => Token::RDoubleArrow, - "+" => Token::Plus, - "-" => Token::Minus, - "_" => Token::Underscore, - "->" => Token::RArrow, - "*" => Token::Star, - "&" => Token::Ampersand, - "!" => Token::Bang, - ":" => Token::Colon, - ";" => Token::Semicolon, - "," => Token::Comma, - "[" => Token::LBracket, - "]" => Token::RBracket, - "(" => Token::LParen, - ")" => Token::RParen, - "{" => Token::LBrace, - "}" => Token::RBrace, - "." => Token::Dot, - "#" => Token::Hash, - "$" => Token::Dollar, - "@" => Token::At, - } -} diff --git a/hir/src/parser/lexer/error.rs b/hir/src/parser/lexer/error.rs deleted file mode 100644 index 033b9faa7..000000000 --- a/hir/src/parser/lexer/error.rs +++ /dev/null @@ -1,85 +0,0 @@ -use core::{fmt, num::IntErrorKind}; - -use miden_diagnostics::{Diagnostic, SourceIndex, SourceSpan, ToDiagnostic}; - -/// Errors that may occur during lexing of the source -#[derive(Clone, Debug, thiserror::Error)] -pub enum LexicalError { - #[error("invalid integer value: {}", DisplayIntErrorKind(reason))] - InvalidInt { - span: SourceSpan, - reason: IntErrorKind, - }, - #[error("encountered unexpected character '{found}'")] - UnexpectedCharacter { start: SourceIndex, found: char }, - #[error("unclosed string")] - UnclosedString { span: SourceSpan }, - #[error("invalid module identifier")] - InvalidModuleIdentifier { span: SourceSpan }, - #[error("invalid function identifier")] - InvalidFunctionIdentifier { span: SourceSpan }, -} -impl PartialEq for LexicalError { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::InvalidInt { reason: lhs, .. }, Self::InvalidInt { reason: rhs, .. }) => { - lhs == rhs - } - ( - Self::UnexpectedCharacter { found: lhs, .. }, - Self::UnexpectedCharacter { found: rhs, .. }, - ) => lhs == rhs, - (Self::UnclosedString { .. }, Self::UnclosedString { .. }) => true, - (Self::InvalidModuleIdentifier { .. }, Self::InvalidModuleIdentifier { .. }) => true, - (Self::InvalidFunctionIdentifier { .. }, Self::InvalidFunctionIdentifier { .. }) => { - true - } - _ => false, - } - } -} -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::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> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { - IntErrorKind::Empty => write!(f, "unable to parse empty string as integer"), - IntErrorKind::InvalidDigit => write!(f, "invalid digit"), - IntErrorKind::PosOverflow => write!(f, "value is too big"), - IntErrorKind::NegOverflow => write!(f, "value is too big"), - IntErrorKind::Zero => write!(f, "zero is not a valid value here"), - other => write!(f, "unable to parse integer value: {:?}", other), - } - } -} diff --git a/hir/src/parser/lexer/mod.rs b/hir/src/parser/lexer/mod.rs deleted file mode 100644 index 9a0f40ca6..000000000 --- a/hir/src/parser/lexer/mod.rs +++ /dev/null @@ -1,524 +0,0 @@ -mod error; -mod token; - -use core::{num::IntErrorKind, ops::Range}; - -use miden_diagnostics::{SourceIndex, SourceSpan}; -use miden_parsing::{Scanner, Source}; -use num_traits::Num; - -use crate::{parser::ParseError, Block, Symbol, Value}; - -pub use self::error::LexicalError; -pub use self::token::Token; - -/// The value produced by the [Lexer] when iterated -pub type Lexed = Result<(SourceIndex, Token, SourceIndex), ParseError>; - -/// Pops a single token from the [Lexer] -macro_rules! pop { - ($lex:ident) => {{ - $lex.skip(); - }}; - ($lex:ident, $code:expr) => {{ - $lex.skip(); - $code - }}; -} - -/// Pops two tokens from the [Lexer] -macro_rules! pop2 { - ($lex:ident) => {{ - $lex.skip(); - $lex.skip(); - }}; - ($lex:ident, $code:expr) => {{ - $lex.skip(); - $lex.skip(); - $code - }}; -} - -/// The lexer that is used to perform lexical analysis on the Miden IR grammar. The lexer implements -/// the `Iterator` trait, so in order to retrieve the tokens, you simply have to iterate over it. -/// -/// # Errors -/// -/// Because the lexer is implemented as an iterator over tokens, this means that you can continue -/// to get tokens even if a lexical error occurs. The lexer will attempt to recover from an error -/// by injecting tokens it expects. -/// -/// 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 { - /// The scanner produces a sequence of chars + location, and can be controlled - /// The location type is SourceIndex - scanner: Scanner, - - /// The most recent token to be lexed. - /// At the start and end, this should be Token::Eof - token: Token, - - /// 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, - - /// 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, - - /// 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, -{ - /// 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(); - let mut lexer = Lexer { - scanner, - token: Token::Eof, - token_start: start + ByteOffset(0), - token_end: start + ByteOffset(0), - eof: false, - }; - lexer.advance(); - lexer - } - - pub fn lex(&mut self) -> Option<::Item> { - if self.eof && self.token == Token::Eof { - return None; - } - - let token = std::mem::replace(&mut self.token, Token::Eof); - let start = self.token_start; - let end = self.token_end; - self.advance(); - match token { - Token::Error(err) => Some(Err(err.into())), - token => Some(Ok((start, token, end))), - } - } - - fn advance(&mut self) { - self.advance_start(); - self.token = self.tokenize(); - } - - #[inline] - fn advance_start(&mut self) { - let mut position: SourceIndex; - loop { - let (pos, c) = self.scanner.read(); - - position = pos; - - if c == '\0' { - self.eof = true; - return; - } - - if c.is_whitespace() { - self.scanner.advance(); - continue; - } - - break; - } - - self.token_start = position; - } - - #[inline] - fn pop(&mut self) -> char { - use miden_diagnostics::ByteOffset; - - let (pos, c) = self.scanner.pop(); - self.token_end = pos + ByteOffset::from_char_len(c); - c - } - - #[inline] - fn peek(&mut self) -> char { - let (_, c) = self.scanner.peek(); - c - } - - #[inline] - fn read(&mut self) -> char { - let (_, c) = self.scanner.read(); - c - } - - #[inline] - fn skip(&mut self) { - self.pop(); - } - - /// Get the span for the current token in `Source`. - #[inline] - fn span(&self) -> SourceSpan { - SourceSpan::new(self.token_start, self.token_end) - } - - #[inline] - fn slice_span(&self, span: impl Into>) -> &str { - self.scanner.slice(span) - } - - /// Get a string slice of the current token. - #[inline] - fn slice(&self) -> &str { - self.scanner.slice(self.span()) - } - - #[inline] - fn skip_whitespace(&mut self) { - let mut c: char; - loop { - c = self.read(); - - if !c.is_whitespace() { - break; - } - - self.skip(); - } - } - - fn tokenize(&mut self) -> Token { - let c = self.read(); - - if c == '/' { - match self.peek() { - '/' => { - self.skip(); - self.skip(); - return self.lex_comment(); - } - c => Token::Error(LexicalError::UnexpectedCharacter { - start: self.span().start(), - found: c, - }), - }; - } - - if c == '\0' { - self.eof = true; - return Token::Eof; - } - - if c.is_whitespace() { - self.skip_whitespace(); - } - - match self.read() { - ',' => pop!(self, Token::Comma), - '.' => pop!(self, Token::Dot), - ':' => pop!(self, Token::Colon), - ';' => pop!(self, Token::Semicolon), - '"' => self.lex_quoted_identifier(), - '(' => pop!(self, Token::LParen), - ')' => pop!(self, Token::RParen), - '[' => pop!(self, Token::LBracket), - ']' => pop!(self, Token::RBracket), - '{' => pop!(self, Token::LBrace), - '}' => pop!(self, Token::RBrace), - '=' => match self.peek() { - '>' => pop2!(self, Token::RDoubleArrow), - _ => pop!(self, Token::Equal), - }, - '+' => self.lex_number(), - '-' => match self.peek() { - '>' => pop2!(self, Token::RArrow), - _ => self.lex_number(), - }, - '*' => pop!(self, Token::Star), - '&' => pop!(self, Token::Ampersand), - '!' => pop!(self, Token::Bang), - '@' => pop!(self, Token::At), - '$' => pop!(self, Token::Dollar), - '#' => pop!(self, Token::Hash), - '0' => match self.peek() { - 'x' => { - self.skip(); - self.skip(); - self.lex_hex() - } - '0'..='9' => self.lex_number(), - _ => pop!(self, Token::Int(0)), - }, - '1'..='9' => self.lex_number(), - 'a'..='z' => self.lex_keyword_or_ident(), - 'A'..='Z' => self.lex_identifier(), - '_' => match self.peek() { - c if c.is_ascii_alphanumeric() || c == '_' => self.lex_identifier(), - _ => pop!(self, Token::Underscore), - }, - c => Token::Error(LexicalError::UnexpectedCharacter { - start: self.span().start(), - found: c, - }), - } - } - - fn lex_comment(&mut self) -> Token { - let mut c; - loop { - c = self.read(); - - if c == '\n' { - break; - } - - if c == '\0' { - self.eof = true; - break; - } - - self.skip(); - } - - Token::Comment - } - - #[inline] - fn lex_keyword_or_ident(&mut self) -> Token { - let c = self.pop(); - debug_assert!(c.is_ascii_alphabetic() && c.is_lowercase()); - - if self.skip_ident(true) { - self.handle_function_ident(self.slice()) - } else { - let s = self.slice(); - if let Some(rest) = s.strip_prefix('v') { - return match rest.parse::() { - Ok(id) => Token::Value(Value::from_u32(id)), - Err(_) => Token::from_keyword_or_ident(s), - }; - } - if let Some(rest) = s.strip_prefix("block") { - return match rest.parse::() { - Ok(id) => Token::Block(Block::from_u32(id)), - Err(_) => Token::from_keyword_or_ident(s), - }; - } - Token::from_keyword_or_ident(self.slice()) - } - } - - fn lex_quoted_identifier(&mut self) -> Token { - use miden_diagnostics::ByteOffset; - - let quote = self.pop(); - debug_assert!(quote == '"' || quote == '\''); - let mut buf = None; - loop { - match self.read() { - '\0' if quote == '"' => { - return Token::Error(LexicalError::UnclosedString { span: self.span() }); - } - c if c == quote => { - let span = self.span().shrink_front(ByteOffset(1)); - - self.skip(); - self.advance_start(); - if self.read() == quote { - self.skip(); - - buf = Some(self.slice_span(span).to_string()); - continue; - } - - let symbol = if let Some(mut buf) = buf { - buf.push_str(self.slice_span(span)); - Symbol::intern(&buf) - } else { - Symbol::intern(self.slice_span(span)) - }; - - return Token::Ident(symbol); - } - _ => { - self.skip(); - continue; - } - } - } - } - - #[inline] - fn lex_identifier(&mut self) -> Token { - let c = self.pop(); - debug_assert!(c.is_ascii_alphabetic() || c == '_'); - - if self.skip_ident(false) { - self.handle_function_ident(self.slice()) - } else { - Token::Ident(Symbol::intern(self.slice())) - } - } - - // Returns true if the identifier is a namespaced identifier (contains double colons), false otherwise - fn skip_ident(&mut self, allow_dot: bool) -> bool { - let mut is_namespaced = false; - loop { - match self.read() { - '_' => self.skip(), - '.' if allow_dot => { - // We only allow '.' when followed by a alpha character - match self.peek() { - c if c.is_ascii_alphabetic() => self.skip(), - _ => break, - } - } - '0'..='9' => self.skip(), - ':' => match self.peek() { - ':' => { - is_namespaced = true; - self.skip(); - self.skip() - } - _ => break, - }, - c if c.is_ascii_alphabetic() => self.skip(), - _ => break, - } - } - is_namespaced - } - - fn handle_function_ident(&self, s: &str) -> Token { - if let Some((offset, c)) = s - .char_indices() - .find(|(_, c)| *c == '.' || c.is_whitespace()) - { - Token::Error(LexicalError::UnexpectedCharacter { - start: self.span().start() + offset, - found: c, - }) - } else { - match s.rsplit_once("::").unwrap() { - (_, function) if function.is_empty() => { - Token::Error(LexicalError::InvalidFunctionIdentifier { span: self.span() }) - } - (module, _) if module.is_empty() => { - Token::Error(LexicalError::InvalidModuleIdentifier { span: self.span() }) - } - (module, function) => { - Token::FunctionIdent((Symbol::intern(module), Symbol::intern(function))) - } - } - } - } - - #[inline] - fn lex_number(&mut self) -> Token { - use num_bigint::BigInt; - - let mut num = String::new(); - - // Expect the first character to be a digit or sign - let c = self.read(); - debug_assert!(c == '-' || c == '+' || c.is_ascii_digit()); - if c == '-' { - num.push(self.pop()); - } else if c == '+' { - self.skip(); - } - - while let '0'..='9' = self.read() { - num.push(self.pop()); - } - - match num.parse::() { - Ok(value) => Token::Int(value), - Err(err) => match err.kind() { - IntErrorKind::PosOverflow | IntErrorKind::NegOverflow => { - let value = BigInt::from_str_radix(&num, 10).expect("invalid bigint"); - Token::BigInt(value) - } - reason => Token::Error(LexicalError::InvalidInt { - span: self.span(), - reason: reason.clone(), - }), - }, - } - } - - #[inline] - fn lex_hex(&mut self) -> Token { - let mut res: Vec = Vec::new(); - - // Expect the first character to be a valid hexadecimal digit - debug_assert!(self.read().is_ascii_hexdigit()); - - loop { - // If we hit a non-hex digit, we're done - let c1 = self.read(); - if !c1.is_ascii_hexdigit() { - break; - } - self.skip(); - - // All hex-encoded bytes are zero-padded, and thus occur - // in pairs, if we observe a non-hex digit at this point, - // it is invalid - let c2 = self.read(); - if !c2.is_ascii_hexdigit() { - return Token::Error(LexicalError::InvalidInt { - span: self.span(), - reason: IntErrorKind::InvalidDigit, - }); - } - self.skip(); - - // The data is big-endian, so shift the first char left by 4 bits, and - // add to the value of the second char - let byte = (c1.to_digit(16).unwrap() << 4) + c2.to_digit(16).unwrap(); - res.push(byte as u8); - } - - Token::Hex(res.into()) - } -} - -impl Iterator for Lexer -where - S: Source, -{ - type Item = Lexed; - - fn next(&mut self) -> Option { - let last = self.token.clone(); - let mut res = self.lex(); - while let Some(Ok((_, Token::Comment, _))) = res { - res = self.lex(); - } - match res { - Some(Ok((start, Token::FunctionIdent((mid, fid)), end))) => { - match last { - // If we parse a namespaced identifier right after the `module` or `kernel` keyword, - // it is a module name, not a function name, so convert it into a Ident token when this - // happens. - Token::Module | Token::Kernel => { - let module_name = format!("{}::{}", mid, fid); - let module_id = Symbol::intern(module_name); - Some(Ok((start, Token::Ident(module_id), end))) - } - _ => Some(Ok((start, Token::FunctionIdent((mid, fid)), end))), - } - } - res => res, - } - } -} diff --git a/hir/src/parser/lexer/token.rs b/hir/src/parser/lexer/token.rs deleted file mode 100644 index e628176e7..000000000 --- a/hir/src/parser/lexer/token.rs +++ /dev/null @@ -1,514 +0,0 @@ -use core::{fmt, mem}; - -use num_bigint::BigInt; - -use super::LexicalError; -use crate::Symbol; - -/// The token type produced by [Lexer] -#[derive(Debug, Clone)] -pub enum Token { - Eof, - Comment, - Error(LexicalError), - /// A module or value identifier - Ident(Symbol), - /// A possibly-qualified function identifier - FunctionIdent((Symbol, Symbol)), - /// A value is an identifier of the form `v(0|[1-9][0-9]*)` - Value(crate::Value), - /// A block is an identifer of the form `(blk(0|[1-9][0-9]*))` - Block(crate::Block), - /// Fixed-width integers smaller than or equal to the platform native integer width - Int(isize), - /// Represents large integer types, such as i128 or u256 - BigInt(BigInt), - /// Hex strings are used to initialize global variables - Hex(crate::ConstantData), - Kernel, - Module, - Internal, - Odr, - Extern, - External, - Pub, - Fn, - Cc, - Fast, - Sret, - Zext, - Sext, - Trunc, - Ret, - Call, - Syscall, - Br, - CondBr, - Switch, - Test, - Load, - MemCpy, - Asm, - MemoryGrow, - AddUnchecked, - AddChecked, - AddOverflowing, - AddWrapping, - SubUnchecked, - SubChecked, - SubOverflowing, - SubWrapping, - MulUnchecked, - MulChecked, - MulOverflowing, - MulWrapping, - DivChecked, - DivUnchecked, - ModUnchecked, - ModChecked, - DivModUnchecked, - DivModChecked, - Min, - Max, - Exp, - And, - BAnd, - Or, - BOr, - Xor, - BXor, - ShlUnchecked, - ShlChecked, - ShlWrapping, - ShlOverflowing, - ShrUnchecked, - ShrChecked, - ShrWrapping, - ShrOverflowing, - Rotl, - Rotr, - Eq, - Neq, - Gt, - Gte, - Lt, - Lte, - Store, - Inv, - IncrUnchecked, - IncrChecked, - IncrWrapping, - IncrOverflowing, - Pow2, - Not, - BNot, - PopCnt, - IsOdd, - Cast, - PtrToInt, - IntToPtr, - Neg, - ConstI1, - ConstI8, - ConstU8, - ConstI16, - ConstU16, - ConstI32, - ConstU32, - ConstI64, - ConstU64, - ConstFelt, - Select, - Assert, - Assertz, - AssertEq, - Alloca, - Unreachable, - Global, - GlobalSymbol, - GlobalLoad, - GlobalIAdd, - As, - Id, - Symbol, - IAdd, - I1, - I8, - U8, - I16, - U16, - I32, - U32, - I64, - U64, - I128, - U128, - U256, - F64, - Felt, - Const, - Mut, - DoubleQuote, - Colon, - Semicolon, - Comma, - Dot, - LParen, - RParen, - LBracket, - RBracket, - LBrace, - RBrace, - Equal, - RDoubleArrow, - Plus, - Minus, - Underscore, - RArrow, - Star, - Ampersand, - Bang, - Dollar, - Hash, - At, -} -impl Token { - pub fn from_keyword_or_ident(s: &str) -> Self { - match s { - "kernel" => Self::Kernel, - "module" => Self::Module, - "internal" => Self::Internal, - "odr" => Self::Odr, - "extern" => Self::Extern, - "external" => Self::External, - "pub" => Self::Pub, - "fn" => Self::Fn, - "cc" => Self::Cc, - "fast" => Self::Fast, - "sret" => Self::Sret, - "zext" => Self::Zext, - "sext" => Self::Sext, - "trunc" => Self::Trunc, - "ret" => Self::Ret, - "call" => Self::Call, - "syscall" => Self::Syscall, - "br" => Self::Br, - "condbr" => Self::CondBr, - "switch" => Self::Switch, - "test" => Self::Test, - "load" => Self::Load, - "memcpy" => Self::MemCpy, - "asm" => Self::Asm, - "memory.grow" => Self::MemoryGrow, - "add.unchecked" => Self::AddUnchecked, - "add.checked" => Self::AddChecked, - "add.overflowing" => Self::AddOverflowing, - "add.wrapping" => Self::AddWrapping, - "sub.unchecked" => Self::SubUnchecked, - "sub.checked" => Self::SubChecked, - "sub.overflowing" => Self::SubOverflowing, - "sub.wrapping" => Self::SubWrapping, - "mul.unchecked" => Self::MulUnchecked, - "mul.checked" => Self::MulChecked, - "mul.overflowing" => Self::MulOverflowing, - "mul.wrapping" => Self::MulWrapping, - "div.unchecked" => Self::DivUnchecked, - "div.checked" => Self::DivChecked, - "mod.unchecked" => Self::ModUnchecked, - "mod.checked" => Self::ModChecked, - "divmod.unchecked" => Self::DivModUnchecked, - "divmod.checked" => Self::DivModChecked, - "min" => Self::Min, - "max" => Self::Max, - "exp" => Self::Exp, - "and" => Self::And, - "band" => Self::BAnd, - "or" => Self::Or, - "bor" => Self::BOr, - "xor" => Self::Xor, - "bxor" => Self::BXor, - "shl.unchecked" => Self::ShlUnchecked, - "shl.checked" => Self::ShlChecked, - "shl.wrapping" => Self::ShlWrapping, - "shl.overflowing" => Self::ShlOverflowing, - "shr.unchecked" => Self::ShrUnchecked, - "shr.checked" => Self::ShrChecked, - "shr.wrapping" => Self::ShlWrapping, - "shr.overflowing" => Self::ShlOverflowing, - "rotl" => Self::Rotl, - "rotr" => Self::Rotr, - "eq" => Self::Eq, - "neq" => Self::Neq, - "gt" => Self::Gt, - "gte" => Self::Gte, - "lt" => Self::Lt, - "lte" => Self::Lte, - "store" => Self::Store, - "inv" => Self::Inv, - "incr.unchecked" => Self::IncrUnchecked, - "incr.checked" => Self::IncrChecked, - "incr.wrapping" => Self::IncrWrapping, - "incr.overflowing" => Self::IncrOverflowing, - "pow2" => Self::Pow2, - "not" => Self::Not, - "bnot" => Self::BNot, - "popcnt" => Self::PopCnt, - "is_odd" => Self::IsOdd, - "cast" => Self::Cast, - "ptrtoint" => Self::PtrToInt, - "inttoprt" => Self::IntToPtr, - "neg" => Self::Neg, - "const.i1" => Self::ConstI1, - "const.i8" => Self::ConstI8, - "const.u8" => Self::ConstU8, - "const.i16" => Self::ConstI16, - "const.u16" => Self::ConstU16, - "const.i32" => Self::ConstI32, - "const.u32" => Self::ConstU32, - "const.i64" => Self::ConstI64, - "const.u64" => Self::ConstU64, - "select" => Self::Select, - "assert" => Self::Assert, - "assertz" => Self::Assertz, - "assert.eq" => Self::AssertEq, - "alloca" => Self::Alloca, - "unreachable" => Self::Unreachable, - "as" => Self::As, - "id" => Self::Id, - "global" => Self::Global, - "global.symbol" => Self::GlobalSymbol, - "global.load" => Self::GlobalLoad, - "global.iadd" => Self::GlobalIAdd, - "symbol" => Self::Symbol, - "iadd" => Self::IAdd, - "i1" => Self::I1, - "i8" => Self::I8, - "u8" => Self::U8, - "i16" => Self::I16, - "u16" => Self::U16, - "i32" => Self::I32, - "u32" => Self::U32, - "i64" => Self::I64, - "u64" => Self::U64, - "i128" => Self::I128, - "u128" => Self::U128, - "u256" => Self::U256, - "f64" => Self::F64, - "felt" => Self::Felt, - "mut" => Self::Mut, - "const" => Self::Const, - other => Self::Ident(Symbol::intern(other)), - } - } -} -impl Eq for Token {} -impl PartialEq for Token { - fn eq(&self, other: &Token) -> bool { - match self { - Self::Int(i) => { - if let Self::Int(i2) = other { - return *i == *i2; - } - } - Self::BigInt(i) => { - if let Self::BigInt(i2) = other { - return *i == *i2; - } - } - Self::Error(_) => { - if let Self::Error(_) = other { - return true; - } - } - Self::Ident(i) => { - if let Self::Ident(i2) = other { - return i == i2; - } - } - Self::FunctionIdent((m1, f1)) => { - if let Self::FunctionIdent((m2, f2)) = other { - return m1 == m2 && f1 == f2; - } - } - Self::Hex(a) => { - if let Self::Hex(b) = other { - return a == b; - } - } - Self::Value(a) => { - if let Self::Value(b) = other { - return a == b; - } - } - Self::Block(a) => { - if let Self::Block(b) = other { - return a == b; - } - } - _ => return mem::discriminant(self) == mem::discriminant(other), - } - false - } -} -impl fmt::Display for Token { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Eof => write!(f, "EOF"), - Self::Comment => write!(f, "COMMENT"), - Self::Error(_) => write!(f, "ERROR"), - Self::Ident(ref id) => write!(f, "{id}"), - Self::FunctionIdent((ref module, ref function)) => write!(f, "{module}::{function}"), - Self::Value(ref id) => write!(f, "{id}"), - Self::Block(ref id) => write!(f, "{id}"), - Self::Int(ref i) => write!(f, "{i}"), - Self::BigInt(ref i) => write!(f, "{i}"), - Self::Hex(ref data) => write!(f, "{data}"), - Self::Kernel => write!(f, "kernel"), - Self::Module => write!(f, "module"), - Self::Internal => write!(f, "internal"), - Self::Odr => write!(f, "odr"), - Self::Extern => write!(f, "extern"), - Self::External => write!(f, "external"), - Self::Pub => write!(f, "pub"), - Self::Fn => write!(f, "fn"), - Self::Cc => write!(f, "cc"), - Self::Fast => write!(f, "fast"), - Self::Sret => write!(f, "sret"), - Self::Zext => write!(f, "zext"), - Self::Sext => write!(f, "sext"), - Self::Trunc => write!(f, "trunc"), - Self::Ret => write!(f, "ret"), - Self::Call => write!(f, "call"), - Self::Syscall => write!(f, "syscall"), - Self::Br => write!(f, "br"), - Self::CondBr => write!(f, "condbr"), - Self::Switch => write!(f, "switch"), - Self::Test => write!(f, "test"), - Self::Load => write!(f, "load"), - Self::MemCpy => write!(f, "memcpy"), - Self::Asm => write!(f, "asm"), - Self::MemoryGrow => write!(f, "memory.grow"), - Self::AddUnchecked => write!(f, "add.unchecked"), - Self::AddChecked => write!(f, "add.checked"), - Self::AddOverflowing => write!(f, "add.overflowing"), - Self::AddWrapping => write!(f, "add.wrapping"), - Self::SubUnchecked => write!(f, "sub.unchecked"), - Self::SubChecked => write!(f, "sub.checked"), - Self::SubOverflowing => write!(f, "sub.overflowing"), - Self::SubWrapping => write!(f, "sub.wrapping"), - Self::MulUnchecked => write!(f, "mul.unchecked"), - Self::MulChecked => write!(f, "mul.checked"), - Self::MulOverflowing => write!(f, "mul.overflowing"), - Self::MulWrapping => write!(f, "mul.wrapping"), - Self::DivUnchecked => write!(f, "div.unchecked"), - Self::DivChecked => write!(f, "div.checked"), - Self::ModUnchecked => write!(f, "mod.unchecked"), - Self::ModChecked => write!(f, "mod.checked"), - Self::DivModUnchecked => write!(f, "divmod.unchecked"), - Self::DivModChecked => write!(f, "divmod.checked"), - Self::Min => write!(f, "min"), - Self::Max => write!(f, "max"), - Self::Exp => write!(f, "exp"), - Self::And => write!(f, "and"), - Self::BAnd => write!(f, "band"), - Self::Or => write!(f, "or"), - Self::BOr => write!(f, "bor"), - Self::Xor => write!(f, "xor"), - Self::BXor => write!(f, "bxor"), - Self::ShlUnchecked => write!(f, "shl.unchecked"), - Self::ShlChecked => write!(f, "shl.checked"), - Self::ShlWrapping => write!(f, "shl.wrapping"), - Self::ShlOverflowing => write!(f, "shl.overflowing"), - Self::ShrUnchecked => write!(f, "shr.unchecked"), - Self::ShrChecked => write!(f, "shr.checked"), - Self::ShrWrapping => write!(f, "shr.wrapping"), - Self::ShrOverflowing => write!(f, "shr.overflowing"), - Self::Rotl => write!(f, "rotl"), - Self::Rotr => write!(f, "rotr"), - Self::Eq => write!(f, "eq"), - Self::Neq => write!(f, "neq"), - Self::Gt => write!(f, "gt"), - Self::Gte => write!(f, "gte"), - Self::Lt => write!(f, "lt"), - Self::Lte => write!(f, "lte"), - Self::Store => write!(f, "store"), - Self::Inv => write!(f, "inv"), - Self::IncrUnchecked => write!(f, "incr.unchecked"), - Self::IncrChecked => write!(f, "incr.checked"), - Self::IncrWrapping => write!(f, "incr.wrapping"), - Self::IncrOverflowing => write!(f, "incr.overflowing"), - Self::Pow2 => write!(f, "pow2"), - Self::Not => write!(f, "not"), - Self::BNot => write!(f, "bnot"), - Self::PopCnt => write!(f, "popcnt"), - Self::IsOdd => write!(f, "is_odd"), - Self::Cast => write!(f, "cast"), - Self::PtrToInt => write!(f, "ptrtoint"), - Self::IntToPtr => write!(f, "inttoptr"), - Self::Neg => write!(f, "neg"), - Self::ConstI1 => write!(f, "const.i1"), - Self::ConstI8 => write!(f, "const.i8"), - Self::ConstU8 => write!(f, "const.u8"), - Self::ConstI16 => write!(f, "const.i16"), - Self::ConstU16 => write!(f, "const.u16"), - Self::ConstI32 => write!(f, "const.i32"), - Self::ConstU32 => write!(f, "const.u32"), - Self::ConstI64 => write!(f, "const.i64"), - Self::ConstU64 => write!(f, "const.u64"), - Self::ConstFelt => write!(f, "const.felt"), - Self::Select => write!(f, "select"), - Self::Assert => write!(f, "assert"), - Self::Assertz => write!(f, "assertz"), - Self::AssertEq => write!(f, "assert.eq"), - Self::Alloca => write!(f, "alloca"), - Self::Unreachable => write!(f, "unreachable"), - Self::Global => write!(f, "global"), - Self::GlobalSymbol => write!(f, "global.symbol"), - Self::GlobalLoad => write!(f, "global.load"), - Self::GlobalIAdd => write!(f, "global.iadd"), - Self::As => write!(f, "as"), - Self::Id => write!(f, "id"), - Self::Symbol => write!(f, "symbol"), - Self::IAdd => write!(f, "iadd"), - Self::I1 => write!(f, "i1"), - Self::I8 => write!(f, "i8"), - Self::U8 => write!(f, "u8"), - Self::I16 => write!(f, "i16"), - Self::U16 => write!(f, "u16"), - Self::I32 => write!(f, "i32"), - Self::U32 => write!(f, "u32"), - Self::I64 => write!(f, "i64"), - Self::U64 => write!(f, "u64"), - Self::I128 => write!(f, "i128"), - Self::U128 => write!(f, "u128"), - Self::U256 => write!(f, "u256"), - Self::F64 => write!(f, "f64"), - Self::Felt => write!(f, "felt"), - Self::Mut => write!(f, "mut"), - Self::Const => write!(f, "const"), - Self::DoubleQuote => write!(f, "\""), - Self::Colon => write!(f, ":"), - Self::Semicolon => write!(f, ";"), - Self::Comma => write!(f, ","), - Self::Dot => write!(f, "."), - Self::LParen => write!(f, "("), - Self::RParen => write!(f, ")"), - Self::LBracket => write!(f, "["), - Self::RBracket => write!(f, "]"), - Self::LBrace => write!(f, "{{"), - Self::RBrace => write!(f, "}}"), - Self::Equal => write!(f, "="), - Self::RDoubleArrow => write!(f, "=>"), - Self::Plus => write!(f, "+"), - Self::Minus => write!(f, "-"), - Self::Underscore => write!(f, "_"), - Self::RArrow => write!(f, "->"), - Self::Star => write!(f, "*"), - Self::Ampersand => write!(f, "&"), - Self::Bang => write!(f, "!"), - Self::Dollar => write!(f, "$"), - Self::Hash => write!(f, "#"), - Self::At => write!(f, "@"), - } - } -} diff --git a/hir/src/parser/mod.rs b/hir/src/parser/mod.rs deleted file mode 100644 index 2cc2bca3f..000000000 --- a/hir/src/parser/mod.rs +++ /dev/null @@ -1,160 +0,0 @@ -/// Simple macro used in the grammar definition for constructing spans -macro_rules! span { - ($l:expr, $r:expr) => { - miden_diagnostics::SourceSpan::new($l, $r) - }; - ($i:expr) => { - miden_diagnostics::SourceSpan::new($i, $i) - }; -} - -pub mod ast; -mod error; -mod lexer; -#[cfg(test)] -mod tests; - -lalrpop_mod!( - #[allow(clippy::all)] - grammar, - "/parser/grammar.rs" -); - -use std::path::Path; -use std::sync::Arc; - -use miden_diagnostics::SourceFile; -use miden_parsing::{FileMapSource, Scanner, Source}; - -pub use self::error::ParseError; -use self::{ - ast::ConvertAstToHir, - lexer::{Lexed, Lexer}, -}; - -pub type ParseResult = Result; - -/// This is the parser for HIR text format -pub struct Parser<'a> { - session: &'a midenc_session::Session, -} -impl<'a> Parser<'a> { - /// Construct a new [Parser] - pub fn new(session: &'a midenc_session::Session) -> Self { - Self { session } - } - - /// Parse a `T` from a source file stored in the current code map - pub fn parse(&self, source: Arc) -> ParseResult - where - T: Parse, - { - ::parse(self, FileMapSource::new(source)) - } - - /// Parse a `T` from a string - pub fn parse_str(&self, source: impl AsRef) -> ParseResult - where - T: Parse, - { - let id = self - .session - .codemap - .add("nofile", source.as_ref().to_string()); - let file = self.session.codemap.get(id).unwrap(); - self.parse(file) - } - - /// Parse a `T` from the given file path - pub fn parse_file(&self, path: impl AsRef) -> ParseResult - where - 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(); - self.parse(file) - } -} - -pub trait Parse: Sized { - type Grammar; - - fn parse(parser: &Parser, source: impl Source) -> ParseResult { - let scanner = Scanner::new(source); - let lexer = Lexer::new(scanner); - - Self::parse_tokens(parser, lexer) - } - - fn parse_tokens(parser: &Parser, tokens: impl IntoIterator) -> ParseResult; -} -impl Parse for ast::Module { - type Grammar = grammar::ModuleParser; - - fn parse_tokens(parser: &Parser, tokens: impl IntoIterator) -> ParseResult { - let mut next_var = 0; - let result = ::Grammar::new().parse( - &parser.session.diagnostics, - &parser.session.codemap, - &mut next_var, - tokens, - ); - match result { - Ok(ast) => { - if parser.session.diagnostics.has_errors() { - return Err(ParseError::Failed); - } - Ok(ast) - } - 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}; - - let mut next_var = 0; - let result = ::Grammar::new() - .parse( - &parser.session.diagnostics, - &parser.session.codemap, - &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, - }) - } - 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/input/test.hir b/hir/src/parser/tests/input/test.hir deleted file mode 100644 index 60ef0e05b..000000000 --- a/hir/src/parser/tests/input/test.hir +++ /dev/null @@ -1,16 +0,0 @@ -module "test" - -const $0 = 0xdeadbeef; - -global internal @DEADBEEF : u32 = $0 { id = 0 }; - -pub cc(fast) fn foo(u32, sext u32) -> u32 { -block0(v1: u32, v2: u32): - v3 = add.unchecked v1, v2 : u32; - br block1; - -block1: - ret v3; -} - -extern cc(kernel) fn tuple::make_pair(sret *mut { u32, u32 }); diff --git a/hir/src/parser/tests/mod.rs b/hir/src/parser/tests/mod.rs deleted file mode 100644 index 60028a0fd..000000000 --- a/hir/src/parser/tests/mod.rs +++ /dev/null @@ -1,167 +0,0 @@ -use miden_diagnostics::{SourceSpan, Span}; - -use crate::parser::ast::*; -use crate::{ - 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, - ) - }; -} - -mod utils; -use self::utils::ParseTest; - -/// This test tries to exercise a broad swath of the IR syntax -#[test] -fn parser_integration_test() { - let dummy_sourcespan = SourceSpan::UNKNOWN; - - // global internal @DEADBEEF : u32 = 0xdeadbeef { id = 0 }; - let deadbeef_const_id = crate::Constant::from_u32(0); - let deadbeef_const = ConstantDeclaration::new( - dummy_sourcespan, - deadbeef_const_id, - "deadbeef".parse().unwrap(), - ); - let deadbeef = GlobalVarDeclaration::new( - dummy_sourcespan, - crate::GlobalVariable::from_u32(0), - ident!(DEADBEEF), - Type::U32, - Linkage::Internal, - Some(deadbeef_const_id), - ); - - // pub cc(fast) fn foo(u32, sext u32) -> u32 { - let mut foo = FunctionDeclaration { - span: dummy_sourcespan, - attrs: Default::default(), - name: ident!(foo), - signature: Signature { - params: vec![ - AbiParam::new(Type::U32), - AbiParam { - ty: Type::U32, - purpose: Default::default(), - extension: ArgumentExtension::Sext, - }, - ], - results: vec![AbiParam::new(Type::U32)], - cc: CallConv::Fast, - linkage: Linkage::External, - }, - blocks: vec![], - }; - - // blk0(v1: u32, v2: u32): - let v1 = crate::Value::from_u32(1); - let v2 = crate::Value::from_u32(2); - let blk0_id = crate::Block::from_u32(0); - let mut blk0 = Block { - span: dummy_sourcespan, - id: blk0_id, - params: vec![ - TypedValue::new(dummy_sourcespan, v1, Type::U32), - TypedValue::new(dummy_sourcespan, v2, Type::U32), - ], - body: vec![], - }; - - // v3 = add.unchecked v1, v2 : u32 - let v3 = crate::Value::from_u32(3); - let inst1 = Inst { - span: dummy_sourcespan, - ty: InstType::BinaryOp { - opcode: Opcode::Add, - overflow: Some(Overflow::Unchecked), - operands: [ - Operand::Value(Span::new(dummy_sourcespan, v1)), - Operand::Value(Span::new(dummy_sourcespan, v2)), - ], - }, - outputs: vec![TypedValue::new(dummy_sourcespan, v3, Type::U32)], - }; - blk0.body.push(inst1); - - // br blk1 - let blk1_id = crate::Block::from_u32(1); - let inst2 = Inst { - span: dummy_sourcespan, - ty: InstType::Br { - opcode: Opcode::Br, - successor: Successor { - span: dummy_sourcespan, - id: blk1_id, - args: vec![], - }, - }, - outputs: vec![], - }; - blk0.body.push(inst2); - - // blk1: - let mut blk1 = Block { - span: dummy_sourcespan, - id: blk1_id, - params: vec![], - body: vec![], - }; - // ret v3 - let inst3 = Inst { - span: dummy_sourcespan, - ty: InstType::Ret { - opcode: Opcode::Ret, - operands: vec![Operand::Value(Span::new(dummy_sourcespan, v3))], - }, - outputs: vec![], - }; - blk1.body.push(inst3); - - foo.blocks.push(blk0); - foo.blocks.push(blk1); - - // cc(kernel) fn tuple::make_pair (sret *mut { u32, u32 }); - let tuple = StructType::new([Type::U32, Type::U32]); - let make_pair = ExternalFunction { - id: FunctionIdent { - module: ident!(tuple), - function: ident!(make_pair), - }, - signature: Signature { - params: vec![AbiParam { - ty: Type::Ptr(Box::new(Type::Struct(tuple))), - purpose: ArgumentPurpose::StructReturn, - extension: Default::default(), - }], - results: vec![], - cc: CallConv::Kernel, - linkage: Linkage::External, - }, - }; - - let expected = Module { - span: dummy_sourcespan, - name: ident!(test), - constants: vec![deadbeef_const], - global_vars: vec![deadbeef], - functions: vec![foo], - externals: vec![Span::new(dummy_sourcespan, make_pair)], - is_kernel: false, - }; - - ParseTest::new().expect_module_ast_from_file("src/parser/tests/input/test.hir", expected); -} - -/// Round-trip an IR module through the textual format and assert that we get back the same module -#[allow(unused)] -fn roundtrip(module: &crate::Module) { - let formatted = module.to_string(); - ParseTest::new().expect_module(&formatted, module); -} diff --git a/hir/src/parser/tests/utils.rs b/hir/src/parser/tests/utils.rs deleted file mode 100644 index b3d79e28b..000000000 --- a/hir/src/parser/tests/utils.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::path::Path; -use std::sync::Arc; - -use miden_diagnostics::{Emitter, Verbosity}; -use midenc_session::{Options, Warnings}; -use pretty_assertions::assert_eq; - -use crate::{ - parser::ast::Module, - parser::{ParseError, Parser}, - testing::TestContext, -}; - -struct SplitEmitter { - capture: miden_diagnostics::CaptureEmitter, - default: miden_diagnostics::DefaultEmitter, -} -impl SplitEmitter { - #[inline] - pub fn new() -> Self { - use miden_diagnostics::term::termcolor::ColorChoice; - - Self { - capture: Default::default(), - default: miden_diagnostics::DefaultEmitter::new(ColorChoice::Auto), - } - } - - #[allow(unused)] - pub fn captured(&self) -> String { - self.capture.captured() - } -} -impl Emitter for SplitEmitter { - #[inline] - fn buffer(&self) -> miden_diagnostics::term::termcolor::Buffer { - self.capture.buffer() - } - - #[inline] - fn print(&self, buffer: miden_diagnostics::term::termcolor::Buffer) -> std::io::Result<()> { - use std::io::Write; - - let mut copy = self.capture.buffer(); - copy.write_all(buffer.as_slice())?; - self.capture.print(buffer)?; - self.default.print(copy) - } -} - -/// [ParseTest] is a container for the data required to run parser tests. Used to build an AST from -/// the given source string and asserts that executing the test will result in the expected AST. -/// -/// # Errors: -/// - ScanError test: check that the source provided contains valid characters and keywords. -/// - ParseError test: check that the parsed values are valid. -/// * InvalidInt: This error is returned if the parsed number is not a valid u64. -pub struct ParseTest { - context: TestContext, - emitter: Arc, -} - -impl ParseTest { - /// Creates a new test, from the source string. - pub fn new() -> Self { - let emitter = Arc::new(SplitEmitter::new()); - let options = Options::new(std::env::current_dir().unwrap()) - .with_verbosity(Verbosity::Warning) - .with_warnings(Warnings::Error); - let context = TestContext::default_with_opts_and_emitter(options, Some(emitter.clone())); - Self { context, emitter } - } - - /// This adds a new in-memory file to the [CodeMap] for this test. - /// - /// This is used when we want to write a test with imports, without having to place files on disk - #[allow(unused)] - pub fn add_virtual_file>(&self, name: P, content: String) { - self.context.session.codemap.add(name.as_ref(), content); - } - - 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 { - let parser = Parser::new(&self.context.session); - parser.parse_str::(source) - } - - #[allow(unused)] - 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 { - let parser = Parser::new(&self.context.session); - parser.parse_str::(source) - } - - #[track_caller] - #[allow(unused)] - pub fn expect_module_diagnostic(&self, source: &str, expected: &str) { - if let Err(err) = self.parse_module(source) { - self.context.session.diagnostics.emit(err); - assert!( - self.emitter.captured().contains(expected), - "expected diagnostic output to contain the string: '{}'", - expected - ); - } else { - panic!("expected parsing to fail, but it succeeded"); - } - } - - /// Parses a [Module] from the given source string and asserts that executing the test will result - /// in the expected AST. - #[track_caller] - pub fn expect_module(&self, source: &str, expected: &crate::Module) { - match self.parse_module(source) { - Err(err) => { - self.context.session.diagnostics.emit(err); - panic!("expected parsing to succeed, see diagnostics for details"); - } - Ok(parsed) => { - assert_eq!(&parsed, expected); - } - } - } - - /// Parses a [Module] from the given source string and asserts that executing the test will result - /// in the expected AST. - #[track_caller] - #[allow(unused)] - pub fn expect_module_ast(&self, source: &str, expected: Module) { - match self.parse_module_ast(source) { - Err(err) => { - self.context.session.diagnostics.emit(err); - panic!("expected parsing to succeed, see diagnostics for details"); - } - Ok(ast) => assert_eq!(ast, expected), - } - } - - /// Parses a [Module] from the given source path and asserts that executing the test will result - /// in the expected AST. - #[track_caller] - pub fn expect_module_ast_from_file(&self, path: &str, expected: Module) { - match self.parse_module_ast_from_file(path) { - Err(err) => { - self.context.session.diagnostics.emit(err); - panic!("expected parsing to succeed, see diagnostics for details"); - } - Ok(ast) => assert_eq!(ast, expected), - } - } -} diff --git a/hir/src/pass/analysis.rs b/hir/src/pass/analysis.rs deleted file mode 100644 index f1ad2d974..000000000 --- a/hir/src/pass/analysis.rs +++ /dev/null @@ -1,469 +0,0 @@ -use std::any::{Any, TypeId}; -use std::hash::Hash; -use std::rc::Rc; - -use midenc_session::Session; -use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; - -type BuildFxHasher = std::hash::BuildHasherDefault; - -/// 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), -} - -/// A convenient type alias for `Result` -pub type AnalysisResult = Result; - -#[doc(hidden)] -pub trait PreservableAnalysis: Any { - /// Called to determine if this analysis should be invalidated after a pass is run - /// - /// By default, all analyses are always invalidated after a pass, unless that pass - /// specifically marks an analysis as preserved. - /// - /// If overridden, implementors must ensure that they use the provided - /// [PreservedAnalyses] set to determine if any dependencies were invalidated, - /// and return `true` if so. - fn is_invalidated(&self, _analyses: &PreservedAnalyses) -> bool { - true - } -} -impl PreservableAnalysis for A { - fn is_invalidated(&self, analyses: &PreservedAnalyses) -> bool { - ::is_invalidated(self, analyses) - } -} - -/// An [Analysis] computes information about some compiler entity, e.g. a module. -/// -/// Analyses are cached, and associated with a unique key derived from the entity -/// to which they were applied. These cached analyses can then be queried via the -/// [AnalysisManager] by requesting a specific concrete [Analysis] type using that -/// key. -/// -/// For example, a module is typically associated with a unique identifier. Thus -/// to obtain the analysis for a specific module, you would request the specific -/// analysis using the module id, see [AnalysisManager::get]. -pub trait Analysis: Sized + Any { - /// The entity to which this analysis applies - type Entity: AnalysisKey; - - /// Analyze `entity`, using the provided [AnalysisManager] to query other - /// analyses on which this one depends; and the provided [Session] to - /// configure this analysis based on the current compilation session. - fn analyze( - entity: &Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> AnalysisResult; - - /// Called to determine if this analysis should be invalidated after a pass is run - /// - /// By default, all analyses are always invalidated after a pass, unless that pass - /// specifically marks an analysis as preserved. - /// - /// If overridden, implementors must ensure that they use the provided - /// [PreservedAnalyses] set to determine if any dependencies were invalidated, - /// and return `true` if so. - fn is_invalidated(&self, _analyses: &PreservedAnalyses) -> bool { - true - } -} - -/// The [AnalysisKey] trait is implemented for compiler entities that are targeted -/// for one or more [Analysis], and have a stable, unique identifier which can be -/// used to cache the results of each analysis computed for that entity. -/// -/// You must ensure that the key uniquely identifies the entity to which it applies, -/// or incorrect analysis results will be returned from the [AnalysisManager]. Note, -/// however, that it is not necessary to ensure that the key reflect changes to the -/// underlying entity (see [AnalysisManager::invalidate] for how invalidation is done). -pub trait AnalysisKey: 'static { - /// The type of the unique identifier associated with `Self` - type Key: Hash + PartialEq + Eq; - - /// Get the key to associate with the current entity - fn key(&self) -> Self::Key; -} - -/// This type is used as a cache key for analyses cached by the [AnalysisManager]. -/// -/// It pairs the [TypeId] of the [Analysis], with the hash of the entity type and -/// the unique key associated with the specific instance of the entity type to which -/// the analysis applies. This ensures that for any given analysis/entity combination, -/// no two cache entries will have the same key unless the analysis key for the entity -/// matches. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct CachedAnalysisKey { - ty: TypeId, - key: u64, -} -impl CachedAnalysisKey { - /// Get a new cache key from the given type information and analysis key - fn new(key: &<::Entity as AnalysisKey>::Key) -> Self - where - A: Analysis, - { - let ty = TypeId::of::(); - let key = Self::entity_key::<::Entity>(key); - Self { ty, key } - } - - fn entity_key(key: &::Key) -> u64 - where - T: AnalysisKey, - { - use core::hash::Hasher; - - let mut hasher = FxHasher::default(); - let entity_ty = TypeId::of::(); - entity_ty.hash(&mut hasher); - key.hash(&mut hasher); - hasher.finish() - } -} - -/// [PreservedAnalyses] represents the set of analyses which will be preserved for the next pass. -/// -/// You may mark an analysis as preserved using [AnalysisManager::mark_preserved]. -#[derive(Default)] -pub struct PreservedAnalyses { - current_entity_key: u64, - preserved: FxHashMap>, -} -impl PreservedAnalyses { - fn new( - current_entity_key: u64, - mut cached: FxHashMap>, - preserve: FxHashSet, - ) -> Self { - // Since we know which analyses are definitely preserved, - // build the initial preserved analyses set from those. - let mut preserved = Self::with_capacity(current_entity_key, preserve.len()); - for key in preserve.into_iter() { - if let Some(analysis) = cached.remove(&key) { - preserved.insert(key, analysis); - } - } - - // Preserve all analyses for other entities - let mut worklist = vec![]; - for (key, analysis) in cached.into_iter() { - if key.key != current_entity_key { - preserved.insert(key, analysis); - continue; - } - worklist.push((key, analysis)); - } - - // Ask all remaining analyses if they should indeed be invalidated. - // - // We iterate to a fixpoint here to ensure that any new preserved analyses - // are taken into account by the remaining analyses pending invalidation - // when their `is_invalidated` method is called. If those analyses depend - // on a newly preserved analysis, they may be able to avoid being invalidated - // if they have no other invalidated dependencies. - let mut q = vec![]; - let mut changed = false; - loop { - while let Some((key, analysis)) = worklist.pop() { - if analysis.is_invalidated(&preserved) { - q.push((key, analysis)); - continue; - } else { - changed = true; - preserved.insert(key, analysis); - } - } - if !changed { - break; - } - changed = false; - core::mem::swap(&mut worklist, &mut q); - } - - preserved - } - - /// Returns true if the analysis associated with the given type and key is preserved - pub fn is_preserved(&self) -> bool - where - A: Analysis, - { - let ty = TypeId::of::(); - let key = CachedAnalysisKey { - ty, - key: self.current_entity_key, - }; - self.preserved.contains_key(&key) - } - - #[inline] - fn insert(&mut self, key: CachedAnalysisKey, analysis: Rc) { - self.preserved.insert(key, analysis); - } - - fn with_capacity(current_entity_key: u64, cap: usize) -> Self { - use std::collections::HashMap; - - Self { - current_entity_key, - preserved: HashMap::with_capacity_and_hasher(cap, BuildFxHasher::default()), - } - } -} - -/// The [AnalysisManager] is used to query and compute analyses required during compilation. -/// -/// Each thread gets its own analysis manager, and may query any analysis, as long as the -/// caller has the key used for caching that analysis (e.g. module identifier). -/// -/// To compute an analysis, one must have a reference to the entity on which the analysis -/// is applied, and request that the analysis be computed. -/// -/// Analyses are cached, and assumed valid until explicitly invalidated. An analysis should -/// be invalidated any time the underlying entity changes, unless the analysis is known to -/// be preserved even with those changes. -#[derive(Default)] -pub struct AnalysisManager { - /// We store the analysis results as `Rc` so that we can freely hand out references - /// to the analysis results without having to concern ourselves with too much lifetime - /// management. - /// - /// Since an [AnalysisManager] is scoped to a single thread, the reference counting - /// overhead is essentially irrelevant. - cached: FxHashMap>, - /// The set of analyses to preserve after the current pass is run - preserve: FxHashSet, - /// The set of entity keys that have had `preserve_none` set - preserve_none: FxHashSet, - /// The set of entity keys that have had `preserve_all` set - preserve_all: FxHashSet, -} -impl AnalysisManager { - /// Get a new, empty [AnalysisManager]. - pub fn new() -> Self { - Self::default() - } - - /// Check if the given analysis has been computed and is in the cache - pub fn is_available(&self, key: &<::Entity as AnalysisKey>::Key) -> bool - where - A: Analysis, - { - let key = CachedAnalysisKey::new::(key); - self.cached.contains_key(&key) - } - - /// Get a reference to the analysis of the requested type, for the given entity, if available - pub fn get(&self, key: &<::Entity as AnalysisKey>::Key) -> Option> - where - A: Analysis, - { - let key = CachedAnalysisKey::new::(key); - self.cached - .get(&key) - .cloned() - .map(preservable_analysis_to_concrete) - } - - /// Get a reference to the analysis of the requested type, for the given entity, or panics with `msg` - pub fn expect(&self, key: &<::Entity as AnalysisKey>::Key, msg: &str) -> Rc - where - A: Analysis, - { - let key = CachedAnalysisKey::new::(key); - self.cached - .get(&key) - .cloned() - .map(preservable_analysis_to_concrete) - .expect(msg) - } - - /// Get a reference to the analysis of the requested type, or the default value, for the given entity, if available - /// - /// If unavailable, and the default value is returned, that value is not cached. - pub fn get_or_default(&self, key: &<::Entity as AnalysisKey>::Key) -> Rc - where - A: Analysis + Default, - { - let key = CachedAnalysisKey::new::(key); - self.cached - .get(&key) - .cloned() - .map(preservable_analysis_to_concrete) - .unwrap_or_else(|| Rc::new(A::default())) - } - - /// Get a reference to the analysis of the requested type, computing it if necessary - /// - /// If computing the analysis fails, `Err` is returned. - pub fn get_or_compute( - &mut self, - entity: &::Entity, - session: &Session, - ) -> AnalysisResult> - where - A: Analysis, - { - let key = CachedAnalysisKey::new::(&entity.key()); - if let Some(cached) = self.cached.get(&key).cloned() { - return Ok(preservable_analysis_to_concrete(cached)); - } - let analysis = Rc::new(A::analyze(entity, self, session)?); - let any = Rc::clone(&analysis); - self.cached.insert(key, any); - Ok(analysis) - } - - /// If an analysis of the requested type has been computed, take ownership of it, - /// and return the owned object to the caller. - /// - /// If there are outstanding references to the cached analysis data, then the data - /// will be cloned so that the caller gets an owning reference. - /// - /// If the analysis has not been computed, returns `None` - pub fn take(&mut self, key: &<::Entity as AnalysisKey>::Key) -> Option - where - A: Analysis + Clone, - { - let key = CachedAnalysisKey::new::(key); - let cached = preservable_analysis_to_concrete(self.cached.remove(&key)?); - Some(match Rc::try_unwrap(cached) { - Ok(analysis) => analysis, - Err(cached) => (*cached).clone(), - }) - } - - /// Insert an analysis into the manager with the given key - pub fn insert(&mut self, key: <::Entity as AnalysisKey>::Key, analysis: A) - where - A: Analysis, - { - let key = CachedAnalysisKey::new::(&key); - self.cached.insert(key, Rc::new(analysis)); - } - - /// Mark all analyses as invalidated, unless otherwise preserved, forcing recomputation - /// of those analyses the next time they are requested. - /// - /// This clears any preservation markers that were set prior to calling this function, - /// e.g. with `mark_preserved`. When this function returns, all analyses are assumed - /// to be invalidated the next time this function is called, unless otherwise indicated. - pub fn invalidate(&mut self, key: &::Key) - where - T: AnalysisKey, - { - use std::collections::HashMap; - - let current_entity_key = CachedAnalysisKey::entity_key::(key); - - if self.preserve_none.remove(¤t_entity_key) { - self.cached.retain(|k, _| k.key != current_entity_key); - return; - } - - if self.preserve_all.remove(¤t_entity_key) { - return; - } - - let mut to_preserve = vec![]; - for key in self.preserve.iter() { - if key.key == current_entity_key { - to_preserve.push(*key); - } - } - - let mut to_invalidate = vec![]; - for key in self.cached.keys() { - if key.key == current_entity_key { - to_invalidate.push(*key); - } - } - - let mut preserve = FxHashSet::default(); - for key in to_preserve.into_iter() { - preserve.insert(self.preserve.take(&key).unwrap()); - } - - let mut cached = - HashMap::with_capacity_and_hasher(to_invalidate.len(), BuildFxHasher::default()); - for key in to_invalidate.into_iter() { - let (key, value) = self.cached.remove_entry(&key).unwrap(); - cached.insert(key, value); - } - - let preserved = PreservedAnalyses::new(current_entity_key, cached, preserve); - self.cached.extend(preserved.preserved); - } - - /// Mark the given analysis as no longer valid (due to changes to the analyzed entity) - /// - /// You should invalidate analyses any time you modify the IR for that entity, unless - /// you can guarantee that the specific analysis is preserved. - pub fn mark_invalid(&mut self, key: &<::Entity as AnalysisKey>::Key) - where - A: Analysis, - { - let key = CachedAnalysisKey::new::(key); - self.preserve.remove(&key); - self.cached.remove(&key); - } - - /// When called, the current pass is signalling that all analyses should be invalidated - /// after it completes, regardless of any other configuration. - pub fn mark_none_preserved(&mut self, key: &::Key) - where - T: AnalysisKey, - { - let preserve_entity_key = CachedAnalysisKey::entity_key::(key); - self.preserve_none.insert(preserve_entity_key); - self.preserve_all.remove(&preserve_entity_key); - } - - /// When called, the current pass is signalling that all analyses will still be valid - /// after it completes, i.e. it makes no modifications that would invalidate an analysis. - /// - /// Care must be taken when doing this, to ensure that the pass actually does not do - /// anything that would invalidate any analysis results, or miscompiles are likely to - /// occur. - pub fn mark_all_preserved(&mut self, key: &::Key) - where - T: AnalysisKey, - { - let preserve_entity_key = CachedAnalysisKey::entity_key::(key); - self.preserve_all.insert(preserve_entity_key); - self.preserve_none.remove(&preserve_entity_key); - } - - /// When called, the current pass is signalling that the given analysis identified by `key`, - /// will still be valid after it completes. - /// - /// This should only be called when the caller can guarantee that the analysis is truly - /// preserved by the pass, otherwise miscompiles are likely to occur. - pub fn mark_preserved(&mut self, key: &<::Entity as AnalysisKey>::Key) - where - A: Analysis, - { - // If we're preserving everything, or preserving nothing, this is a no-op - let key = CachedAnalysisKey::new::(key); - if self.preserve_all.contains(&key.key) || self.preserve_none.contains(&key.key) { - return; - } - - self.preserve.insert(key); - } -} - -fn preservable_analysis_to_concrete(pa: Rc) -> Rc -where - T: AnalysisKey, - A: Analysis, -{ - let any: Rc = pa; - any.downcast::().expect("invalid cached analysis key") -} diff --git a/hir/src/pass/conversion.rs b/hir/src/pass/conversion.rs deleted file mode 100644 index 30d0068a3..000000000 --- a/hir/src/pass/conversion.rs +++ /dev/null @@ -1,123 +0,0 @@ -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), -} - -/// A convenient type alias for `Result` -pub type ConversionResult = Result; - -/// This is a marker trait for [ConversionPass] impls which also implement [PassInfo] -/// -/// It is automatically implemented for you. -pub trait ConversionPassInfo: PassInfo + ConversionPass {} -impl

ConversionPassInfo for P where P: PassInfo + ConversionPass {} - -/// A [ConversionPass] is a pass which applies a change in representation to some compiler entity. -/// -/// Specifically, this is used to convert between intermediate representations/dialects in the compiler. -/// -/// For example, a conversion pass would be used to lower a `miden_hir::parser::ast::Module` -/// to a `miden_hir::Module`. Each conversion between dialects like this can be thought of -/// as delineating compilation phases (e.g. parsing, semantic analysis, elaboration, optimization, -/// etc.). -pub trait ConversionPass { - type From; - type To; - - /// Apply this conversion to `entity` - fn convert( - &mut self, - entity: Self::From, - analyses: &mut AnalysisManager, - session: &Session, - ) -> ConversionResult; - - /// Chains two conversions together to form a new, fused conversion - fn chain

(self, next: P) -> Chain - where - Self: Sized, - P: ConversionPass, - { - Chain::new(self, next) - } -} -impl ConversionPass for Box

-where - P: ?Sized + ConversionPass, -{ - type From = T; - type To = U; - - fn convert( - &mut self, - entity: Self::From, - analyses: &mut AnalysisManager, - session: &Session, - ) -> ConversionResult { - (**self).convert(entity, analyses, session) - } -} - -type ConversionPassCtor = fn() -> Box>; - -#[doc(hidden)] -pub struct ConversionPassRegistration { - pub name: &'static str, - pub summary: &'static str, - pub description: &'static str, - ctor: ConversionPassCtor, -} -impl ConversionPassRegistration { - pub const fn new

() -> Self - where - P: ConversionPass + PassInfo + Default + 'static, - { - Self { - name:

::FLAG, - summary:

::SUMMARY, - description:

::DESCRIPTION, - ctor: dyn_conversion_pass_ctor::, - } - } - - /// Get the name of the registered pass - #[inline] - pub const fn name(&self) -> &'static str { - self.name - } - - /// Get a summary of the registered pass - #[inline] - pub const fn summary(&self) -> &'static str { - self.summary - } - - /// Get a rich description of the registered pass - #[inline] - pub const fn description(&self) -> &'static str { - self.description - } - - /// Get an instance of the registered pass - #[inline] - pub fn get(&self) -> Box> { - (self.ctor)() - } -} - -fn dyn_conversion_pass_ctor() -> Box> -where - P: Default + ConversionPass + 'static, -{ - Box::

::default() -} diff --git a/hir/src/pass/mod.rs b/hir/src/pass/mod.rs deleted file mode 100644 index e9ae7c58c..000000000 --- a/hir/src/pass/mod.rs +++ /dev/null @@ -1,214 +0,0 @@ -//! This module provides traits and associated types for use in compiler pass pipelines. -//use miden_hir_pass::RewritePassRegistration; - -// Register rewrite passes for modules -//inventory::collect!(RewritePassRegistration); - -// Register rewrite passes for functions -//inventory::collect!(RewritePassRegistration); -/* -macro_rules! register_function_rewrite { - ($name:literal, $ty:ty) => { - impl $ty { - fn new( - _options: std::sync::Arc, - _diagnostics: std::sync::Arc, - ) -> Box { - Box::new(crate::ModuleRewritePassAdapter(Self)) - } - } - inventory::submit! { - crate::ModuleRewritePassRegistration::new($name, <$ty>::new) - } - }; -} - */ - -mod analysis; -mod conversion; -mod rewrite; - -pub use self::analysis::*; -pub use self::conversion::*; -pub use self::rewrite::*; - -use midenc_session::Session; - -/// This trait provides descriptive information about a pass -/// -/// This is primarily intended to assist in registering passes with the pass manager -pub trait PassInfo { - /// The string which should be used as the name of this pass on the command line. - /// - /// For example, for the InlineBlocks pass, this is set to `inline-blocks`. - /// - /// You can add `#[derive(PassInfo)]` to a pass type, and this will be derived from - /// the name of the type, and converted to kebab-case, e.g. `inline-blocks`. - const FLAG: &'static str; - /// A short, single-line description of what this pass does. - /// - /// You can add `#[derive(PassInfo)]` to a pass type, and this will be derived from - /// the first line of the documentation attached to the type. - const SUMMARY: &'static str; - /// A rich, potentially multi-line description of this pass and its configuration. - /// - /// You can add `#[derive(PassInfo)]` to a pass type, and this will be derived from - /// the documentation attached to the type. - const DESCRIPTION: &'static str; -} - -/// The [Pass] trait represents a fallible operation which takes an input of any type, and produces an -/// output of any type. This is intentionally abstract, and is intended as a building block for compiler -/// pipelines. -/// -/// [Pass] is in fact so abstract, that it is automatically implemented for any Rust function whose type -/// is representable by `FnMut(I) -> Result`. -/// -/// Implementations of [Pass] can be combined via [Pass::chain], which returns an instantiation of the -/// [Chain] type that itself implements [Pass]. This permits any number of passes to be combined/chained -/// together and passed around as a value. -pub trait Pass { - type Input<'a>; - type Output<'a>; - type Error; - - /// Runs the pass on the given input - /// - /// Passes should return `Err` to signal that the pass has failed - /// and compilation should be aborted - fn run<'a>( - &mut self, - input: Self::Input<'a>, - analyses: &mut AnalysisManager, - session: &Session, - ) -> Result, Self::Error>; - - /// Chains two passes together to form a new, fused pass - fn chain

(self, pass: P) -> Chain - where - Self: Sized, - P: for<'a> Pass = Self::Output<'a>, Error = Self::Error>, - { - Chain::new(self, pass) - } -} -impl Pass for &mut P -where - P: for<'a> Pass = T, Output<'a> = U, Error = E>, -{ - type Input<'a> = T; - type Output<'a> = U; - type Error = E; - - fn run<'a>( - &mut self, - input: Self::Input<'a>, - analyses: &mut AnalysisManager, - session: &Session, - ) -> Result, Self::Error> { - (*self).run(input, analyses, session) - } -} -impl Pass for Box

-where - P: ?Sized + for<'a> Pass = T, Output<'a> = U, Error = E>, -{ - type Input<'a> = T; - type Output<'a> = U; - type Error = E; - - fn run<'a>( - &mut self, - input: Self::Input<'a>, - analyses: &mut AnalysisManager, - session: &Session, - ) -> Result, Self::Error> { - (**self).run(input, analyses, session) - } -} -impl Pass for dyn FnMut(T, &mut AnalysisManager, &Session) -> Result { - type Input<'a> = T; - type Output<'a> = U; - type Error = E; - - #[inline] - fn run<'a>( - &mut self, - input: Self::Input<'a>, - analyses: &mut AnalysisManager, - session: &Session, - ) -> Result, Self::Error> { - self(input, analyses, session) - } -} - -/// [Chain] represents a pipeline of two or more passes whose inputs and outputs are linked -/// together into a "chain". If any pass in the pipeline raises an error, the rest of the -/// pipeline is skipped, and the error is returned. -/// -/// This is not meant to be constructed or referenced directly, as the type signature gets out -/// of hand quickly when combining multiple passes. Instead, you should invoke `chain` on a -/// [Pass] implementation, and use it as a trait object. In some cases this may require boxing -/// the `Chain`, depending on how you are using it in your compiler. -pub struct Chain { - a: A, - b: B, -} -impl Chain { - fn new(a: A, b: B) -> Self { - Self { a, b } - } -} -impl Copy for Chain -where - A: Copy, - B: Copy, -{ -} -impl Clone for Chain -where - A: Clone, - B: Clone, -{ - #[inline] - fn clone(&self) -> Self { - Self::new(self.a.clone(), self.b.clone()) - } -} -impl Pass for Chain -where - A: for<'a> Pass, - B: for<'a> Pass = ::Output<'a>, Error = E>, -{ - type Input<'a> = ::Input<'a>; - type Output<'a> = ::Output<'a>; - type Error = ::Error; - - fn run<'a>( - &mut self, - input: Self::Input<'a>, - analyses: &mut AnalysisManager, - session: &Session, - ) -> Result, Self::Error> { - let output = self.a.run(input, analyses, session)?; - self.b.run(output, analyses, session) - } -} -impl ConversionPass for Chain -where - A: ConversionPass, - B: ConversionPass::To>, -{ - type From = ::From; - type To = ::To; - - fn convert<'a>( - &mut self, - entity: Self::From, - analyses: &mut AnalysisManager, - session: &Session, - ) -> ConversionResult { - let output = self.a.convert(entity, analyses, session)?; - self.b.convert(output, analyses, session) - } -} diff --git a/hir/src/pass/rewrite.rs b/hir/src/pass/rewrite.rs deleted file mode 100644 index 6d744826f..000000000 --- a/hir/src/pass/rewrite.rs +++ /dev/null @@ -1,336 +0,0 @@ -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), -} - -/// A convenient type alias for `Result<(), RewriteError>` -pub type RewriteResult = Result<(), RewriteError>; - -/// This is a marker trait for [RewritePass] impls which also implement [PassInfo] -/// -/// It is automatically implemented for you. -pub trait RewritePassInfo: PassInfo + RewritePass {} -impl

RewritePassInfo for P where P: PassInfo + RewritePass {} - -/// A [RewritePass] is a pass which transforms/rewrites an entity without converting it to a -/// new representation. For conversions, see [crate::ConversionPass]. -/// -/// For example, a rewrite rule which applies a mangling scheme to function names, does not -/// change the representation of a function, it simply changes things about the existing -/// representation (e.g. the name in this example). -/// -/// A rewrite is given access to the current [AnalysisManager], which can be used to obtain -/// the results of some [Analysis] needed to perform the rewrite, as well as indicate to the -/// [AnalysisManager] which analyses are preserved by the rewrite, if any. -/// -/// Additionally, the current [midenc_session::Session] is provided, which can be used as a -/// source of configuration for the rewrite, if needed. -pub trait RewritePass { - /// The entity type to which this rewrite applies - type Entity: AnalysisKey; - - /// Returns true if this rewrite should be applied to `entity` - fn should_apply(&self, _entity: &Self::Entity, _session: &Session) -> bool { - true - } - - /// Apply this rewrite to `entity` - fn apply( - &mut self, - entity: &mut Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> RewriteResult; - - /// Apply this rewrite, then `next` as a pipeline of rewrites - fn chain(self, next: R) -> RewriteSet - where - Self: Sized + 'static, - R: RewritePass + 'static, - { - RewriteSet::pair(self, next) - } -} - -impl RewritePass for Box

-where - T: AnalysisKey, - P: RewritePass, -{ - type Entity = T; - - fn should_apply(&self, entity: &Self::Entity, session: &Session) -> bool { - (**self).should_apply(entity, session) - } - - fn apply( - &mut self, - entity: &mut Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> RewriteResult { - (**self).apply(entity, analyses, session) - } - - fn chain(self, next: R) -> RewriteSet - where - Self: Sized + 'static, - R: RewritePass + 'static, - { - let mut rewrites = RewriteSet::from(self); - rewrites.push(next); - rewrites - } -} -impl RewritePass for dyn FnMut(&mut T, &mut AnalysisManager, &Session) -> RewriteResult -where - T: AnalysisKey, -{ - type Entity = T; - - #[inline] - fn apply( - &mut self, - entity: &mut Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> RewriteResult { - self(entity, analyses, session) - } -} - -/// This type is used to adapt function [RewritePass] to apply against a module. -/// -/// When this is applied to a module, all functions in the module will be rewritten. -pub struct ModuleRewritePassAdapter(R); -impl Default for ModuleRewritePassAdapter -where - R: RewritePass + Default, -{ - fn default() -> Self { - Self(R::default()) - } -} -impl ModuleRewritePassAdapter -where - R: RewritePass, -{ - /// Adapt `R` to run against all functions in a [crate::Module] - pub const fn new(pass: R) -> Self { - Self(pass) - } -} -impl PassInfo for ModuleRewritePassAdapter { - const FLAG: &'static str = ::FLAG; - const SUMMARY: &'static str = ::SUMMARY; - const DESCRIPTION: &'static str = ::DESCRIPTION; -} -impl RewritePass for ModuleRewritePassAdapter -where - R: RewritePass, -{ - type Entity = crate::Module; - - fn apply( - &mut self, - module: &mut Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> RewriteResult { - // Removing a function via this cursor will move the cursor to - // the next function in the module. Once the end of the module - // is reached, the cursor will point to the null object, and - // `remove` will return `None`. - let mut cursor = module.cursor_mut(); - let mut dirty = false; - while let Some(mut function) = cursor.remove() { - // Apply rewrite - if self.0.should_apply(&function, session) { - dirty = true; - self.0.apply(&mut function, analyses, session)?; - } else { - analyses.mark_all_preserved::(&function.id); - } - // Add the function back to the module - // - // We add it before the current position of the cursor - // to ensure that we don't interfere with our traversal - // of the module top to bottom - cursor.insert_before(function); - } - - if !dirty { - analyses.mark_all_preserved::(&module.name); - } - - Ok(()) - } -} - -/// A [RewriteSet] is used to compose two or more [RewritePass] impls for the same entity type, -/// to be applied as a single, fused [RewritePass]. -pub struct RewriteSet { - rewrites: Vec>>, -} -impl Default for RewriteSet { - fn default() -> Self { - Self { rewrites: vec![] } - } -} -impl RewriteSet -where - T: AnalysisKey, -{ - /// Create a new [RewriteSet] from a pair of [RewritePass] - pub fn pair(a: A, b: B) -> Self - where - A: RewritePass + 'static, - B: RewritePass + 'static, - { - Self { - rewrites: vec![Box::new(a), Box::new(b)], - } - } - - /// Append a new [RewritePass] to this set - pub fn push(&mut self, rewrite: R) - where - R: RewritePass + 'static, - { - self.rewrites.push(Box::new(rewrite)); - } - - /// Take all rewrites out of another [RewriteSet], and append them to this set - pub fn append(&mut self, other: &mut Self) { - self.rewrites.append(&mut other.rewrites); - } - - /// Extend this rewrite set with rewrites from `iter` - pub fn extend(&mut self, iter: impl IntoIterator>>) { - self.rewrites.extend(iter); - } -} -impl From>> for RewriteSet -where - T: AnalysisKey, -{ - fn from(rewrite: Box>) -> Self { - Self { - rewrites: vec![rewrite], - } - } -} -impl + 'static> From> for RewriteSet -where - T: AnalysisKey, -{ - fn from(rewrite: Box) -> Self { - Self { - rewrites: vec![rewrite], - } - } -} -impl RewritePass for RewriteSet -where - T: AnalysisKey, -{ - type Entity = T; - - fn apply( - &mut self, - entity: &mut Self::Entity, - analyses: &mut AnalysisManager, - session: &Session, - ) -> RewriteResult { - for pass in self.rewrites.iter_mut() { - // Skip the rewrite if it shouldn't be applied - if !pass.should_apply(entity, session) { - continue; - } - - // Apply the rewrite - pass.apply(entity, analyses, session)?; - // Invalidate all analyses that were not marked preserved by `pass` - analyses.invalidate::(&entity.key()); - } - - Ok(()) - } - - fn chain(mut self, next: R) -> RewriteSet - where - Self: Sized + 'static, - R: RewritePass + 'static, - { - self.push(next); - self - } -} - -#[doc(hidden)] -pub struct RewritePassRegistration { - pub name: &'static str, - pub summary: &'static str, - pub description: &'static str, - ctor: fn() -> Box>, -} -impl RewritePassRegistration { - pub const fn new

() -> Self - where - P: RewritePass + PassInfo + Default + 'static, - { - Self { - name:

::FLAG, - summary:

::SUMMARY, - description:

::DESCRIPTION, - ctor: dyn_rewrite_pass_ctor::

, - } - } - - /// Get the name of the registered pass - #[inline] - pub const fn name(&self) -> &'static str { - self.name - } - - /// Get a summary of the registered pass - #[inline] - pub const fn summary(&self) -> &'static str { - self.summary - } - - /// Get a rich description of the registered pass - #[inline] - pub const fn description(&self) -> &'static str { - self.description - } - - /// Get an instance of the registered pass - #[inline] - pub fn get(&self) -> Box> { - (self.ctor)() - } -} - -fn dyn_rewrite_pass_ctor

() -> Box::Entity>> -where - P: RewritePass + Default + 'static, -{ - Box::

::default() -} - -// Register rewrite passes for modules -inventory::collect!(RewritePassRegistration); - -// Register rewrite passes for functions -inventory::collect!(RewritePassRegistration); diff --git a/hir/src/program/linker.rs b/hir/src/program/linker.rs deleted file mode 100644 index d594998d9..000000000 --- a/hir/src/program/linker.rs +++ /dev/null @@ -1,584 +0,0 @@ -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), -} - -/// Represents a node in the global variable dependency graph -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -enum Node { - /// A global symbol that was defined or has been referenced - Global(Ident), - /// A function which refers to one or more global symbols - Function(FunctionIdent), -} - -/// 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`. -/// -/// As a (very) rough overview, a typical linker is given a set of object files containing machine -/// code and data, one for every translation unit participating in the link (e.g. for Rust the translation -/// unit is a single crate). The linker will also be informed when dependencies are expected to be provided -/// at runtime, i.e. dynamic libraries. With this, a linker does the following: -/// -/// * Determines the final layout of all code and data in the executable or library being produced, -/// this allows the linker to know the absolute and/or relative address for every symbol in the program. -/// * Ensures that all referenced symbols (functions/globals) are defined, or that there are runtime -/// dependencies that will satisfy the missing symbols (in practice, what actually happens is the -/// static linker, i.e. `ld`, assumes missing symbols will be provided by the runtime dependencies, -/// and it is the runtime dynamic linker, i.e. `rtdyld`, which handles the case where those symbols -/// cannot be located when the program is starting up). -/// * Rewrites instructions with symbol references to use the absolute/relative addressing once the -/// layout of the program in memory is known. -/// * Emits the linked program in binary form, either as an executable or as a library -/// -/// However, there a couple of things that make [Linker] somewhat different than your typical system linker: -/// -/// * We do not emit assembly/run the assembler prior to linking. This is because Miden Assembly (MASM) -/// does not have a way to represent things like data segments or global variables natively. Instead, the -/// linker is responsible for laying those out in memory ahead of time, and then all operations involving -/// them are lowered to use absolute addresses. -/// * [Linker] does not emit the final binary form of the program. It still plans the layout of program data -/// in memory, and performs the same type of validations as a typical linker, but the output of the linker -/// is a [Program], which must be emitted as Miden Assembly in a separate step _after_ being linked. -/// * We cannot guarantee that the [Program] we emit constitutes a closed set of modules/functions, even -/// accounting for functions whose definitions will be provided at runtime. This is because the Miden VM -/// acts as the final assembler of the programs it runs, and if the [Program] we emit is used as a library, -/// we can't know what other modules might end up being linked into the final program run by the VM. As a -/// result, it is assumed that any code introduced separately is either: -/// 1. memory-agnostic, i.e. it doesn't use the heap and/or make any assumptions about the heap layout. -/// 2. compatible with the layout decided upon by the linker, i.e. it uses well-known allocator functions -/// like `malloc`; or it places its memory in the range 2^30-2^31 for user contexts, or 2^30-(2^32 - 2^30) -/// for root contexts (the latter allocates a separate region for syscall locals). The linker will always -/// reserve memory starting at address 2^30 for locals and "unmanaged" memory allocations, to support -/// scenarios whereby a linked library is used with a program that needs its own region of heap to manage. -/// * Miden has separate address spaces depending on the context in which a function is executed, i.e. the root -/// vs user context distinction. Currently, all programs are assumed to be executed in the root 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 { - /// 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 dependency graph for all functions in the program. - /// - /// This graph is used to obtain a topological ordering of the - /// functions in the program, so that we may emit Miden Assembly - /// such that all procedure definitions occur before their uses. - /// - /// It is allowed for there to be cyclical module dependencies, but - /// we do not permit cyclical function dependencies (i.e. recursive - /// function calls). - /// - /// The edge weight is unused. - callgraph: DiGraphMap, - /// This is a subset of `callgraph` for a single module. - /// - /// This is only used when preprocessing a module, and is reset on each call to `add` - local_callgraph: DiGraphMap, - /// This is the dependency graph for all globals in the program. - /// - /// This graph is used to identify what global symbols are used, from where, - /// and whether or not those dependencies are satisfied by the set of modules - /// being linked into the program. - globals: DiGraphMap, - /// 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, -} -impl Default for Linker { - fn default() -> Self { - let mut program = Box::new(Program::new()); - - // We reserve the first page of memory for the shadow stack - program - .segments - .declare(0, 64 * 1024, vec![].into(), false) - .expect("unexpected error declaring shadow stack segment"); - - Self { - program, - pending: Default::default(), - 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> { - if let Some(prev) = self.program.entrypoint() { - if prev != id { - return Err(LinkerError::InvalidMultipleEntry { current: id, prev }); - } - } - - self.program.entrypoint = Some(id); - - Ok(()) - } - - /// Add `module` to the set of modules to be linked - /// - /// This preprocesses the module for the linker, and will catch the following issues: - /// - /// * Multiple modules with the same name - /// * Conflicting data segment declarations - /// * 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> { - let id = module.name; - - // Reset the auxiliary data structures used for preprocessing - self.local_callgraph.clear(); - self.renamed.clear(); - - // 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)); - } - - // Import all data segments - while let Some(segment) = module.segments.pop_front() { - self.program.segments.insert(segment)?; - } - - // Import all globals, and in the process: - // - // * Record all global variable definitions in the dependency graph - // * Track renamed symbols, if any, produced by the import - for global in module.globals.iter() { - let mut imported_global = global.clone(); - // If an initializer was set, we need to import the constant data too - if let Some(init) = imported_global.init.take() { - let data = module.globals.get_constant(init).clone(); - imported_global.init = Some(self.program.globals.insert_constant(data)); - } - let (id, renamed) = self.program.globals.try_insert(imported_global)?; - let name = self.program.globals.get(id).name; - if renamed { - self.renamed.insert(global.name, name); - } - self.globals.add_node(Node::Global(name)); - } - - // Compute a subset of the call graph just for this module - for function in module.functions.iter() { - // While we're here, update the global call graph as well - let caller = self.callgraph.add_node(function.id); - let caller = self.local_callgraph.add_node(caller); - for import in function.imports() { - let callee = self.callgraph.add_node(import.id); - self.callgraph.add_edge(caller, callee, ()); - // For the toposort, we only care about functions in the same module - if import.id.module == module.name { - let callee = self.local_callgraph.add_node(callee); - self.local_callgraph.add_edge(caller, callee, ()); - } - } - } - - // Compute the topographical ordering of functions in this module - let topography = petgraph::algo::toposort(&self.local_callgraph, None).map_err(|_| { - // The error here only gives us the caller, but we'd like to know - // 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) - .expect_err("expected call graph to contain a cycle") - })?; - - // Preprocess all functions in this module by: - // - // * Record dependencies on global symbols in the dependency graph - // * Rewrite any references to renamed global symbols with internal/odr linkage - // * Reorder the function list according to the topological order computed above - // - // We do this in a single pass, by draining the list of sorted function ids, and - // then unlinking each function from the module in that order, processing it, and - // then inserting it at the end of the function list of the module. After all the - // ids have been visited, the function list of the module will be in topographical - // order. This is about as efficient as we can make it, roughly `O(N * M)`, where - // `N` is the number of functions in the module, and `M` is the number of items - // we must skip before we find the next function to process. If the module is in - // topographical order already, the `M` factor is 1. - for id in topography.into_iter() { - // Find and remove the function with the given name from the module - let mut function = module.unlink(id.function); - - // Process the global values of this function, i.e. references to global symbols - for gvalue in function.dfg.globals.values_mut() { - match gvalue { - // This global value type is sensitive to renaming - GlobalValueData::Symbol { - name: ref mut global_name, - .. - } => { - if let Some(new_name) = self.renamed.get(global_name).copied() { - *global_name = new_name; - } - // Make sure the symbol is in the graph - let global_node = self.globals.add_node(Node::Global(*global_name)); - let dependent_node = self.globals.add_node(Node::Function(id)); - // Record a dependency from this function to the named global - self.globals.add_edge(dependent_node, global_node, ()); - } - // These global values are stable even in the face of symbol renaming - GlobalValueData::Load { .. } | GlobalValueData::IAddImm { .. } => continue, - } - } - - // Insert the function back in the list - module.functions.push_back(function); - } - - // We're done preprocessing, so add the module to the pending set - self.pending.insert(id, 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. - /// - /// When called, all of the added modules have been preprocessed, and what remains are the - /// following tasks: - /// - /// * Verify that all referenced modules exist, or are known to be provided at runtime - /// * Verify that all referenced functions exist, or are known to be provided at runtime, - /// and that the signature known to the caller matches the actual definition. - /// * Verifies that the entrypoint, if set, is valid - /// * Verify that there are no cycles in the call graph, i.e. that there is no recursion present - /// * Verify that all references to global symbols have corresponding definitions - /// * Perform garbage collection of unreferenced globals - /// * 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> { - // Ensure linker-defined globals and intrinsics are present - self.populate_builtins(); - - // Look for cycles in the call graph - validate_callgraph(&self.callgraph)?; - - // 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)); - } - } - - // 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 { - continue; - } - - return Err(LinkerError::MissingModule(node.module)); - } - - // 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(); - - // 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)); - } - // 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, - )?; - } - } - - // Verify global symbol references, and garbage collect unused globals - for node in self.globals.nodes() { - // Skip nodes in the graph which aren't globals - let Node::Global(name) = node else { - continue; - }; - - // If this global has no incoming edges, it's dead, so garbage collect it - let mut dependents = self.globals.neighbors_directed(node, Direction::Incoming); - let is_dead = dependents.next().is_none(); - if is_dead { - let id = self - .program - .globals - .find(name) - .expect("expected global to be in table when dead"); - self.program.globals.remove(id); - } - - // If it has dependents, but isn't defined anywhere, raise an error - if !self.program.globals.exists(name) { - return Err(LinkerError::MissingGlobal(name)); - } - } - - // Run the garbage collector - 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); - } - - Ok(self.program) - } - - /// Programs we construct may depend on one or more predefined globals/intrinsics - /// that are provided by the compiler in order to support common functionality, such - /// as memory management primitives. This function handles defining these prior to - /// linking the program. - fn populate_builtins(&mut self) { - // We provide three globals for managing the heap, based on the layout - // of the data segments and these globals. - let globals_offset = self.program.segments.next_available_offset(); - // Compute the start of the heap by finding the end of the globals segment, aligned to the nearest word boundary - let heap_base = globals_offset - .checked_add( - self.program - .globals - .size_in_bytes() - .try_into() - .expect("unable to allocate globals, unable to fit in linear memory"), - ) - .expect("unable to allocate globals, not enough unreserved space available") - .align_up(32); - let hp = heap_base.to_le_bytes(); - // Initialize all 3 globals with the computed heap pointer - let heap_ptr_ty = Type::Ptr(Box::new(Type::U8)); - self.program.globals.declare("HEAP_BASE".into(), heap_ptr_ty.clone(), Linkage::External, Some(hp.into())).expect("unable to declare HEAP_BASE, a conflicting global by that name was already defined"); - self.program - .globals - .declare( - "HEAP_TOP".into(), - heap_ptr_ty.clone(), - Linkage::External, - Some(hp.into()), - ) - .expect( - "unable to declare HEAP_TOP, a conflicting global by that name was already defined", - ); - self.program - .globals - .declare( - "HEAP_END".into(), - heap_ptr_ty, - Linkage::External, - Some(hp.into()), - ) - .expect( - "unable to declare HEAP_END, a conflicting global by that name was already defined", - ); - } - - /// If an executable is being linked, discover unused functions and garbage collect them. - /// - /// Once a function has been identified as dead and is collected, any transitive items it - /// references may also be collected if they are orphaned as a result of the collection. - /// - /// If a library is being linked, this function is a no-op, as it is not known what will - /// be needed at runtime once used in the context of a program. - fn garbage_collect(&mut self) { - // TODO: See https://github.com/0xPolygonMiden/miden-ir/issues/26 - } -} - -/// Verifies that the actual signature of the given function matches what was expected. -/// -/// Here, `actual` is the defined signature of the function; while `expected` is the signature -/// associated with an [ExternalFunction], i.e. it is the signature expected by a prospective -/// caller. -fn verify_matching_signature( - id: FunctionIdent, - actual: &Signature, - expected: &Signature, -) -> Result<(), LinkerError> { - // If the number of parameters differs, raise an error - if expected.arity() != actual.arity() { - return Err(LinkerError::SignatureMismatch(id)); - } - - // If the type or specification of any parameters differs, raise an error - for (ep, ap) in expected.params().iter().zip(actual.params().iter()) { - if !is_matching_param(ep, ap) { - return Err(LinkerError::SignatureMismatch(id)); - } - } - - // If the number of results differs, raise an error - let expected_results = expected.results(); - let actual_results = actual.results(); - if expected_results.len() != actual_results.len() { - return Err(LinkerError::SignatureMismatch(id)); - } - - // If the type of results differs, raise an error - for (er, ar) in expected_results.iter().zip(actual_results.iter()) { - if !is_matching_param(er, ar) { - return Err(LinkerError::SignatureMismatch(id)); - } - } - - Ok(()) -} - -/// Determines if the actual ABI of a parameter matches the expected ABI. -fn is_matching_param(expected: &AbiParam, actual: &AbiParam) -> bool { - if expected.purpose != actual.purpose { - return false; - } - - match actual.extension { - // If the actual definition has no extension requirement, - // it is ok if the caller is more strict, however we may - // want to raise a warning in such cases - ArgumentExtension::None => expected.ty == actual.ty, - ArgumentExtension::Zext if expected.extension != ArgumentExtension::Zext => false, - ArgumentExtension::Sext if expected.extension != ArgumentExtension::Sext => false, - ArgumentExtension::Zext | ArgumentExtension::Sext => expected.ty == actual.ty, - } -} - -/// 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> { - 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 }), - _ => Ok(()), - }, - ) -} diff --git a/hir/src/program/mod.rs b/hir/src/program/mod.rs deleted file mode 100644 index f749ae85d..000000000 --- a/hir/src/program/mod.rs +++ /dev/null @@ -1,302 +0,0 @@ -mod linker; - -use core::{ - convert::{AsMut, AsRef}, - ops::{Deref, DerefMut}, -}; -use intrusive_collections::RBTree; - -pub use self::linker::{Linker, LinkerError}; - -use super::*; - -/// A [Program] is a collection of [Module]s that are being compiled together as a package. -/// -/// This is primarily used for storing/querying data which must be shared across modules: -/// -/// * The set of global variables which will be allocated on the global heap -/// * The set of modules and functions which have been defined -/// -/// When translating to Miden Assembly, we need something like this to allow us to perform some -/// basic linker tasks prior to emitting the textual MASM which will be fed to the Miden VM. -/// -/// This structure is intended to be allocated via [std::sync::Arc], so that it can be shared -/// across multiple threads which are emitting/compiling modules at the same time. It is designed -/// so that individual fields are locked, rather than the structure as a whole, to minimize contention. -/// The intuition is that, in general, changes at the [Program] level are relatively infrequent, i.e. -/// only when declaring a new [Module], or [GlobalVariable], do we actually need to mutate the structure. -/// In all other situations, changes are scoped at the [Module] level. -#[derive(Default)] -pub struct Program { - /// This tree stores all of the modules being compiled as part of the current program. - modules: RBTree, - /// 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 - /// a program or just a collection of modules; and in the case of the former, what code - /// to emit in the root code block. - /// - /// If not present, but there is a function in the program with the `entrypoint` attribute, - /// that function will be used instead. If there are multiple functions with the `entrypoint` - /// attribute, and this field is `None`, the linker will raise an error. - entrypoint: Option, - /// The data segments gathered from all modules in the program, and laid out in address order. - segments: DataSegmentTable, - /// The global variable table produced by linking the global variable tables of all - /// modules in this program. The layout of this table corresponds to the layout of - /// global variables in the linear memory heap at runtime. - globals: GlobalVariableTable, -} - -impl Program { - /// Create a new, empty [Program]. - #[inline(always)] - pub fn new() -> Self { - Self::default() - } - - /// Returns true if this program has a defined entrypoint - pub fn has_entrypoint(&self) -> bool { - self.entrypoint().is_none() - } - - /// Returns true if this program is executable. - /// - /// An executable program is one which has an entrypoint that will be called - /// after the program is loaded. - pub fn is_executable(&self) -> bool { - self.has_entrypoint() - } - - /// Returns the [FunctionIdent] corresponding to the program entrypoint - pub fn entrypoint(&self) -> Option { - self.entrypoint - .or_else(|| self.modules.iter().find_map(|m| m.entrypoint())) - } - - /// Return a reference to the module table for this program - pub fn modules(&self) -> &RBTree { - &self.modules - } - - /// Return a mutable reference to the module table for this program - pub fn modules_mut(&mut self) -> &mut RBTree { - &mut self.modules - } - - /// Return a reference to the data segment table for this program - pub fn segments(&self) -> &DataSegmentTable { - &self.segments - } - - /// Get a reference to the global variable table for this program - pub fn globals(&self) -> &GlobalVariableTable { - &self.globals - } - - /// Get a mutable reference to the global variable table for this program - pub fn globals_mut(&mut self) -> &mut GlobalVariableTable { - &mut self.globals - } - - /// Returns true if `name` is defined in this program. - pub fn contains(&self, name: Ident) -> bool { - !self.modules.find(&name).is_null() - } - - /// Look up the signature of a function in this program by `id` - pub fn signature(&self, id: &FunctionIdent) -> Option<&Signature> { - let module = self.modules.find(&id.module).get()?; - module.function(id.function).map(|f| &f.signature) - } -} - -#[doc(hidden)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct ProgramAnalysisKey; -impl crate::pass::AnalysisKey for Program { - type Key = ProgramAnalysisKey; - - fn key(&self) -> Self::Key { - ProgramAnalysisKey - } -} - -/// This struct provides an ergonomic way to construct a [Program] in an imperative fashion. -/// -/// 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>, - entry: Option, - diagnostics: &'a miden_diagnostics::DiagnosticsHandler, -} -impl<'a> ProgramBuilder<'a> { - pub fn new(diagnostics: &'a miden_diagnostics::DiagnosticsHandler) -> Self { - Self { - modules: Default::default(), - entry: None, - diagnostics, - } - } - - /// Set the entrypoint for the [Program] being built. - #[inline] - pub fn with_entrypoint(mut self, id: FunctionIdent) -> Self { - self.entry = Some(id); - self - } - - /// Add `module` to the set of modules to link into the final [Program] - /// - /// Unlike `add_module`, this function consumes the current builder state - /// and returns a new one, to allow for chaining builder calls together. - /// - /// Returns `Err` if a module with the same name already exists - pub fn with_module(mut self, module: Box) -> Result { - self.add_module(module).map(|_| self) - } - - /// Add `module` to the set of modules to link into the final [Program] - /// - /// 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)); - } - - self.modules.insert(module_name, module); - - Ok(()) - } - - /// Start building a [Module] with the given name. - /// - /// When the builder is done, the resulting [Module] will be inserted - /// into the set of modules to be linked into the final [Program]. - pub fn module>(&mut self, name: S) -> ProgramModuleBuilder<'_, 'a> { - let name = name.into(); - let module = match self.modules.remove(&name) { - None => Box::new(Module::new(name)), - Some(module) => module, - }; - ProgramModuleBuilder { - pb: self, - mb: ModuleBuilder::from(module), - } - } - - /// Link a [Program] from the current [ProgramBuilder] state - pub fn link(self) -> Result, LinkerError> { - let mut linker = Linker::new(); - 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.link() - } -} - -/// This is used to build a [Module] from a [ProgramBuilder]. -/// -/// It is basically just a wrapper around [ModuleBuilder], but overrides two things: -/// -/// * `build` will add the module to the [ProgramBuilder] directly, rather than returning it -/// * `function` will delegate to [ProgramFunctionBuilder] which plays a similar role to this -/// struct, but for [ModuleFunctionBuilder]. -pub struct ProgramModuleBuilder<'a, 'b: 'a> { - pb: &'a mut ProgramBuilder<'b>, - mb: ModuleBuilder, -} -impl<'a, 'b: 'a> ProgramModuleBuilder<'a, 'b> { - /// Start building a [Function] wwith the given name and signature. - pub fn function<'c, 'd: 'c, S: Into>( - &'d mut self, - name: S, - signature: Signature, - ) -> Result, SymbolConflictError> { - Ok(ProgramFunctionBuilder { - diagnostics: self.pb.diagnostics, - fb: self.mb.function(name, signature)?, - }) - } - - /// Build the current [Module], adding it to the [ProgramBuilder]. - /// - /// Returns `err` if a module with that name already exists. - pub fn build(self) -> Result<(), ModuleConflictError> { - let pb = self.pb; - let mb = self.mb; - - pb.add_module(mb.build())?; - Ok(()) - } -} -impl<'a, 'b: 'a> Deref for ProgramModuleBuilder<'a, 'b> { - type Target = ModuleBuilder; - - fn deref(&self) -> &Self::Target { - &self.mb - } -} -impl<'a, 'b: 'a> DerefMut for ProgramModuleBuilder<'a, 'b> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.mb - } -} -impl<'a, 'b: 'a> AsRef for ProgramModuleBuilder<'a, 'b> { - fn as_ref(&self) -> &ModuleBuilder { - &self.mb - } -} -impl<'a, 'b: 'a> AsMut for ProgramModuleBuilder<'a, 'b> { - fn as_mut(&mut self) -> &mut ModuleBuilder { - &mut self.mb - } -} - -/// 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 -/// [ProgramBuilder]. -pub struct ProgramFunctionBuilder<'a, 'b: 'a> { - diagnostics: &'b miden_diagnostics::DiagnosticsHandler, - fb: ModuleFunctionBuilder<'a>, -} -impl<'a, 'b: 'a> ProgramFunctionBuilder<'a, 'b> { - /// Build the current function - pub fn build(self) -> Result { - let diagnostics = self.diagnostics; - self.fb.build(diagnostics) - } -} -impl<'a, 'b: 'a> Deref for ProgramFunctionBuilder<'a, 'b> { - type Target = ModuleFunctionBuilder<'a>; - - fn deref(&self) -> &Self::Target { - &self.fb - } -} -impl<'a, 'b: 'a> DerefMut for ProgramFunctionBuilder<'a, 'b> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.fb - } -} -impl<'a, 'b: 'a> AsRef> for ProgramFunctionBuilder<'a, 'b> { - fn as_ref(&self) -> &ModuleFunctionBuilder<'a> { - &self.fb - } -} -impl<'a, 'b: 'a> AsMut> for ProgramFunctionBuilder<'a, 'b> { - fn as_mut(&mut self) -> &mut ModuleFunctionBuilder<'a> { - &mut self.fb - } -} diff --git a/hir/src/segments.rs b/hir/src/segments.rs deleted file mode 100644 index 1a27ab5e3..000000000 --- a/hir/src/segments.rs +++ /dev/null @@ -1,289 +0,0 @@ -use std::{ - fmt, - hash::{Hash, Hasher}, -}; - -use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink, UnsafeRef}; - -intrusive_adapter!(pub DataSegmentAdapter = UnsafeRef: DataSegment { link: LinkedListLink }); - -use super::{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)] -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}")] - OverlappingSegments { - offset1: Offset, - size1: u32, - offset2: Offset, - size2: u32, - }, - /// The current segment and a previous definition of that segment do - /// not agree on the data or read/write properties of the memory they - /// represent. - #[error("invalid data segment: segment at {0:#x} conflicts with a previous segment declaration at this address")] - 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. - /// - /// For example, Miden reserves some amount of memory for procedure locals - /// at a predetermined address, and we do not permit segments to be allocated - /// past that point. - #[error("invalid data segment: segment of {size} bytes at {offset:#x} would extend beyond the end of the usable heap")] - 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")] - 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")] - InitOutOfBounds { - offset: Offset, - size: u32, - actual: u32, - }, -} - -/// Similar to [GlobalVariableTable], this structure is used to track data segments in a module or program. -#[derive(Default)] -pub struct DataSegmentTable { - segments: LinkedList, -} -impl Clone for DataSegmentTable { - fn clone(&self) -> Self { - let mut table = Self::default(); - for segment in self.segments.iter() { - table - .segments - .push_back(UnsafeRef::from_box(Box::new(segment.clone()))); - } - table - } -} -impl DataSegmentTable { - /// Returns true if the table has no segments defined - pub fn is_empty(&self) -> bool { - self.segments.is_empty() - } - - /// Returns the offset in linear memory where the last data segment ends - pub fn next_available_offset(&self) -> u32 { - if let Some(last_segment) = self.last() { - let next_offset = last_segment.offset() + last_segment.size(); - // Ensure the start of the globals segment is word-aligned - next_offset.align_up(32) - } else { - 0 - } - } - - /// Declare a new [DataSegment], with the given offset, size, and data. - /// - /// Returns `Err` if the declared segment overlaps/conflicts with an existing segment. - pub fn declare( - &mut self, - offset: Offset, - size: u32, - init: ConstantData, - readonly: bool, - ) -> Result<(), DataSegmentError> { - self.insert(Box::new(DataSegment::new(offset, size, init, readonly)?)) - } - - /// Insert a [DataSegment] into this table, while preserving the order of the table. - /// - /// This will fail if the segment is invalid, or overlaps/conflicts with an existing segment. - pub fn insert(&mut self, segment: Box) -> Result<(), DataSegmentError> { - let mut cursor = self.segments.front_mut(); - let end = segment.offset + segment.size; - while let Some(current_segment) = cursor.get() { - let segment_end = current_segment.offset + current_segment.size; - // If this segment starts after the segment we're declaring, - // we do not need to continue searching for conflicts, and - // can go a head and perform the insert - if current_segment.offset >= end { - cursor.insert_before(UnsafeRef::from_box(segment)); - return Ok(()); - } - // If this segment starts at the same place as the one we're - // declaring that's a guaranteed conflict - if current_segment.offset == segment.offset { - // If the two segments have the same size and offset, then - // if they match in all other respects, we're done. If they - // don't match, then we raise a mismatch error. - if current_segment.size == segment.size - && current_segment.init == segment.init - && current_segment.readonly == segment.readonly - { - return Ok(()); - } - return Err(DataSegmentError::Mismatch(segment.offset)); - } - // This segment starts before the segment we're declaring, - // make sure that this segment ends before our segment starts - if segment_end > segment.offset { - return Err(DataSegmentError::OverlappingSegments { - offset1: segment.offset, - size1: segment.size, - offset2: current_segment.offset, - size2: current_segment.size, - }); - } - - cursor.move_next(); - } - - self.segments.push_back(UnsafeRef::from_box(segment)); - - Ok(()) - } - - /// Traverse the data segments in the table in ascending order by offset - pub fn iter<'a, 'b: 'a>( - &'b self, - ) -> intrusive_collections::linked_list::Iter<'a, DataSegmentAdapter> { - self.segments.iter() - } - - /// Remove the first data segment from the table - #[inline] - pub fn pop_front(&mut self) -> Option> { - self.segments - .pop_front() - .map(|unsafe_ref| unsafe { UnsafeRef::into_box(unsafe_ref) }) - } - - /// Return a reference to the last [DataSegment] in memory - #[inline] - pub fn last(&self) -> Option<&DataSegment> { - self.segments.back().get() - } -} -impl fmt::Debug for DataSegmentTable { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.segments.iter()).finish() - } -} - -/// A [DataSegment] represents a region of linear memory that should be initialized -/// with a given vector of bytes. -/// -/// This is distinct from [GlobalVariableData], which can be referenced by name, -/// and participates in linkage. Furthermore, [GlobalVariableData] is only as large -/// as it's type/initializer and alignment require, they cannot be arbitrarily sized. -/// -/// A data segment has an offset from the start of linear memory, i.e. address 0x0, -/// and a fixed size, which must be at least as large as the initializer data for -/// the segment. If the size is larger than the initializer data, then it is implied -/// that the remaining bytes will be zeroed. -/// -/// A read-only data segment is used to determine whether a given operation is permitted -/// on addresses falling in that segment - e.g. loads are allowed, stores are not. Miden -/// currently does not have any form of memory protection, so this validation is best -/// effort. -#[derive(Clone)] -pub struct DataSegment { - link: LinkedListLink, - /// The offset from the start of linear memory where this segment starts - offset: Offset, - /// The size, in bytes, of this data segment. - /// - /// By default this will be the same size as `init`, unless explicitly given. - size: u32, - /// The data to initialize this segment with, may not be larger than `size` - init: ConstantData, - /// Whether or not this segment is intended to be read-only data - readonly: bool, -} -impl fmt::Debug for DataSegment { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("DataSegment") - .field("offset", &self.offset) - .field("size", &self.size) - .field("init", &format_args!("{}", &self.init)) - .field("readonly", &self.readonly) - .finish() - } -} -impl DataSegment { - /// Create a new [DataSegment] with the given offset, size, initializer, and readonly flag. - /// - /// If the declared size and the size of the initializer differ, then the greater of the two is - /// used. However, if the declared size is smaller than the initializer, an error is returned. - /// - /// If the offset and/or size are invalid, an error is returned. - pub(crate) fn new( - offset: Offset, - size: u32, - init: ConstantData, - readonly: bool, - ) -> Result { - // Require the initializer data to be no larger than 2^32 bytes - let init_size = init - .len() - .try_into() - .map_err(|_| DataSegmentError::InitTooLarge(offset))?; - - // Require the initializer to fit within the declared bounds - if size < init_size { - return Err(DataSegmentError::InitOutOfBounds { - offset, - size, - actual: init_size, - }); - } - - // Require the entire segment to fit within the linear memory address space - let size = core::cmp::max(size, init_size); - offset - .checked_add(size) - .ok_or(DataSegmentError::OutOfBounds { offset, size })?; - - Ok(Self { - link: Default::default(), - offset, - size, - init, - readonly, - }) - } - - /// Get the offset from the base of linear memory where this segment starts - pub const fn offset(&self) -> Offset { - self.offset - } - - /// Get the size, in bytes, of this segment - pub const fn size(&self) -> u32 { - self.size - } - - /// Get a reference to this segment's initializer data - pub const fn init(&self) -> &ConstantData { - &self.init - } - - /// Returns true if this segment is intended to be read-only - pub const fn is_readonly(&self) -> bool { - self.readonly - } -} -impl Eq for DataSegment {} -impl PartialEq for DataSegment { - fn eq(&self, other: &Self) -> bool { - self.offset == other.offset - && self.size == other.size - && self.init == other.init - && self.readonly == other.readonly - } -} -impl Hash for DataSegment { - fn hash(&self, state: &mut H) { - self.offset.hash(state); - self.size.hash(state); - self.init.hash(state); - self.readonly.hash(state); - } -} diff --git a/hir/src/testing.rs b/hir/src/testing.rs deleted file mode 100644 index ff50a8251..000000000 --- a/hir/src/testing.rs +++ /dev/null @@ -1,1185 +0,0 @@ -use std::{mem, path::Path, slice, sync::Arc}; - -use miden_diagnostics::Emitter; -use midenc_session::{Options, Session}; - -use super::*; - -const PAGE_SIZE: u32 = 64 * 1024; - -/// The base context used by all IR tests -pub struct TestContext { - pub session: Session, -} -impl Default for TestContext { - fn default() -> Self { - Self::default_with_emitter(None) - } -} -impl TestContext { - /// Create a new test context with the given [Session] - pub fn new(session: Session) -> Self { - Self { session } - } - - pub fn default_with_emitter(emitter: Option>) -> Self { - Self::default_with_opts_and_emitter(Default::default(), emitter) - } - - pub fn default_with_opts_and_emitter( - options: Options, - emitter: Option>, - ) -> Self { - use midenc_session::InputFile; - - let session = Session::new( - Default::default(), - InputFile::from_path("test.hir").unwrap(), - None, - None, - None, - options, - emitter, - ); - - 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") - } - - /// Get a [SourceSpan] corresponding to the callsite of this function - #[track_caller] - #[inline(never)] - pub fn current_span(&self) -> miden_diagnostics::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()) - } - - /// Get a [SourceSpan] representing the location in the given source file (by id), line and column - /// - /// 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 { - self.session - .codemap - .line_column_to_span(source_id, line - 1, column - 1) - .expect("invalid source location") - } -} - -#[macro_export] -macro_rules! current_file { - () => { - std::path::Path::new(env!("CARGO_MANIFEST_DIR")) - .parent() - .unwrap() - .join(file!()) - }; -} - -#[macro_export] -macro_rules! span { - ($codemap:ident, $src:ident) => { - $codemap - .line_column_to_span($src, line!() - 1, column!() - 1) - .unwrap() - }; -} - -/// pub fn issue_56(i32, i32) -> i32 { -/// block0(v0: i32, v1: i32): -/// v3 = lt v0, v1 : i1; -/// v4 = cast v3 : i32; -/// v5 = neq 0, v4 : i1; -/// v6 = select v5, v0, v1 : i32; -/// v7 = const.i32 0 -/// cond_br v7, block1(v6), block2(v6) -/// -/// block1(v8: i32): -/// v9 = add.wrapping v7, v8 -/// ret v9; -/// -/// block2(v10: i32): -/// v11 = add.wrapping v7, v10 -/// ret v11 -///} -pub fn issue56(builder: &mut ModuleBuilder, context: &TestContext) -> FunctionIdent { - // Declare the `fib` function, with the appropriate type signature - let sig = Signature { - params: vec![AbiParam::new(Type::I32), AbiParam::new(Type::I32)], - results: vec![AbiParam::new(Type::I32)], - cc: CallConv::SystemV, - linkage: Linkage::External, - }; - let mut fb = builder - .function("entrypoint", sig) - .expect("unexpected symbol conflict"); - - let entry = fb.current_block(); - // Get the value for `v0` and `v1` - let (v0, v1) = { - let args = fb.block_params(entry); - (args[0], args[1]) - }; - - let v3 = fb.ins().lt(v0, v1, context.current_span()); - let v4 = fb.ins().cast(v3, Type::I32, context.current_span()); - let v5 = fb - .ins() - .neq_imm(v4, Immediate::I32(0), context.current_span()); - let v6 = fb.ins().select(v5, v0, v1, context.current_span()); - let v7 = fb.ins().i32(0, context.current_span()); - let cond = fb.ins().i1(true, context.current_span()); - - let block1 = fb.create_block(); - let block2 = fb.create_block(); - let v8 = fb.append_block_param(block1, Type::I32, context.current_span()); - let v10 = fb.append_block_param(block2, Type::I32, context.current_span()); - - fb.ins() - .cond_br(cond, block1, &[v6], block2, &[v6], context.current_span()); - - fb.switch_to_block(block1); - let v9 = fb.ins().add_wrapping(v7, v8, context.current_span()); - fb.ins().ret(Some(v9), context.current_span()); - - fb.switch_to_block(block2); - let v11 = fb.ins().add_wrapping(v7, v10, context.current_span()); - fb.ins().ret(Some(v11), context.current_span()); - - // We're done - fb.build(&context.session.diagnostics) - .expect("unexpected validation error, see diagnostics output") -} - -/// Construct an implementation of a function which computes the sum -/// of a Fibonnaci sequence of length `n`, using the provided builder. -/// -/// This function is very simple, does not contain any memory operations, -/// any local variables, or function calls. It makes for a good sanity -/// check. -/// -/// In simple pseudocode, this is the function we're building: -/// -/// ```text,ignore -/// fn fib(n: u32) -> u32 { -/// let mut a = 0; -/// let mut b = 1; -/// for _ in 0..=n { -/// let c = a + b; -/// a = b; -/// b = c; -/// } -/// a -/// } -/// ``` -/// -/// Expressed as IR, we're looking for: -/// -/// ```text,ignore -/// module test -/// -/// pub fn fib(u32) -> u32 { -/// entry(n: u32): -/// a0 = const.u32 0 : u32; -/// b0 = const.u32 1 : u32; -/// n0 = const.u32 0 : u32; -/// br blk0(a0, b0, n0); -/// -/// blk0(a1: u32, b1: u32, n1: u32): -/// continue = lt n1, n : i1; -/// cond_br continue, blk1, blk2(a1); -/// -/// blk1: -/// b2 = add.checked a1, b1 : u32; -/// n2 = incr.wrapping n1 : u32; -/// br blk0(b1, b2, n2); -/// -/// blk2(result: u32): -/// ret result; -/// } -/// ``` -pub fn fib1(builder: &mut ModuleBuilder, context: &TestContext) -> FunctionIdent { - // Declare the `fib` function, with the appropriate type signature - let sig = Signature { - params: vec![AbiParam::new(Type::U32)], - results: vec![AbiParam::new(Type::U32)], - cc: CallConv::SystemV, - linkage: Linkage::External, - }; - let mut fb = builder - .function("fib", sig) - .expect("unexpected symbol conflict"); - - let entry = fb.current_block(); - // Get the value for `n` - let n = { - let args = fb.block_params(entry); - args[0] - }; - - // This block corresponds to `blk0` in the example - let loop_header = fb.create_block(); - let a1 = fb.append_block_param(loop_header, Type::U32, context.current_span()); - let b1 = fb.append_block_param(loop_header, Type::U32, context.current_span()); - let n1 = fb.append_block_param(loop_header, Type::U32, context.current_span()); - - // This block corresponds to `blk1` in the example - let loop_body = fb.create_block(); - - // This block corresponds to `blk2` in the example - let loop_exit = fb.create_block(); - let result = fb.append_block_param(loop_exit, Type::U32, context.current_span()); - - // Now, starting from the entry block, we build out the rest of the function in control flow order - fb.switch_to_block(entry); - let a0 = fb.ins().u32(0, context.current_span()); - let b0 = fb.ins().u32(1, context.current_span()); - let i0 = fb.ins().u32(0, context.current_span()); - fb.ins() - .br(loop_header, &[a0, b0, i0], context.current_span()); - - fb.switch_to_block(loop_header); - let continue_flag = fb.ins().lt(n1, n, context.current_span()); - fb.ins().cond_br( - continue_flag, - loop_body, - &[], - loop_exit, - &[a1], - context.current_span(), - ); - - fb.switch_to_block(loop_body); - let b2 = fb.ins().add_checked(a1, b1, context.current_span()); - let n2 = fb.ins().incr_wrapping(n1, context.current_span()); - fb.ins() - .br(loop_header, &[b1, b2, n2], context.current_span()); - - fb.switch_to_block(loop_exit); - fb.ins().ret(Some(result), context.current_span()); - - // We're done - fb.build(&context.session.diagnostics) - .expect("unexpected validation error, see diagnostics output") -} - -/// Construct an implementation of a function which computes the sum -/// of a matrix of u32 values, with dimensions `rows` by `cols`. -/// -/// This helper does not take a [ModuleBuilder] because it is used in some -/// simpler tests where we just want the function, you can simply add the -/// function to the [ModuleBuilder] directly. -/// -/// This function is very simple, does not contain any memory operations, -/// any local variables, or function calls. It makes for a good sanity -/// check. -/// -/// In simple pseudocode, this is the function we're building: -/// -/// ```text,ignore -/// pub fn sum_matrix(ptr: *mut u32, rows: u32, cols: u32) -> u32 { -/// let mut sum = 0; -/// if ptr.is_null() { return sum; } -/// for r in 0..rows { -/// for col in 0..cols { -/// let value = *ptr[row][col]; -/// sum += value; -/// } -/// } -/// sum -/// } -/// ``` -/// -/// This corresponds to the following IR: -/// -/// ```text,ignore -/// pub fn test(*mut u32, u32, u32) -> *mut u32 { -/// entry(ptr0: *mut u32, rows: u32, cols: u32): -/// sum0 = const.u32 0 : u32 -/// ptr1 = ptrtoint ptr0 : u32 -/// not_null = neq ptr1, 0 : i1 -/// condbr not_null, blk1, blk0(sum0) -/// -/// blk0(result0: u32): -/// ret result0 -/// -/// blk1: -/// rows0 = const.u32 0 : u32 -/// cols0 = const.u32 0 : u32 -/// row_size = mul.checked cols, 4 -/// br blk2(sum0, rows0, cols0) -/// -/// blk2(sum1: u32, rows1: u32, cols1: u32): -/// has_more_rows = lt rows1, rows -/// row_skip = mul.checked rows1, row_size -/// condbr has_more_rows, blk3(sum1, rows1, cols1), blk0(sum1) -/// -/// blk3(sum3: u32, rows3: u32, cols3: u32): -/// has_more_cols = lt cols3, cols -/// condbr has_more_cols, blk4, blk5 -/// -/// blk4: -/// col_skip = mul.checked cols3, 4 -/// skip = add.checked row_skip, col_skip -/// ptr4i = add.checked ptr1, skip -/// ptr4 = inttoptr ptr4i : *mut u32 -/// value = load ptr4 : u32 -/// sum4 = add.checked sum3, value -/// cols4 = incr.wrapping cols3 -/// br blk3(sum4, rows3, cols4) -/// -/// blk5: -/// rows5 = incr.wrapping rows3 -/// br blk2(sum3, rows5, cols3) -/// } -/// ``` -pub fn sum_matrix(builder: &mut ModuleBuilder, context: &TestContext) -> FunctionIdent { - let sig = Signature::new( - [ - AbiParam::new(Type::Ptr(Box::new(Type::U32))), - AbiParam::new(Type::U32), - AbiParam::new(Type::U32), - ], - [AbiParam::new(Type::U32)], - ); - let id = Ident::new(Symbol::intern("sum_matrix"), context.current_span()); - let mut fb = builder - .function(id, sig) - .expect("unexpected symbol conflict"); - - let entry = fb.current_block(); - - let a = fb.create_block(); // blk0(result0: u32) - let result0 = fb.append_block_param(a, Type::U32, context.current_span()); - let b = fb.create_block(); // blk1 - let c = fb.create_block(); // blk2(sum1: u32, rows1: u32, cols1: u32) - let sum1 = fb.append_block_param(c, Type::U32, context.current_span()); - let rows1 = fb.append_block_param(c, Type::U32, context.current_span()); - let cols1 = fb.append_block_param(c, Type::U32, context.current_span()); - let d = fb.create_block(); // blk3(sum3: u32, rows3: u32, cols3: u32) - let sum3 = fb.append_block_param(d, Type::U32, context.current_span()); - let rows3 = fb.append_block_param(d, Type::U32, context.current_span()); - let cols3 = fb.append_block_param(d, Type::U32, context.current_span()); - let e = fb.create_block(); // blk4 - let f = fb.create_block(); // blk5 - - let (ptr0, rows, cols) = { - let args = fb.block_params(entry); - (args[0], args[1], args[2]) - }; - // entry - let sum0 = fb.ins().u32(0, context.current_span()); - let ptr1 = fb.ins().ptrtoint(ptr0, Type::U32, context.current_span()); - let not_null = fb - .ins() - .neq_imm(ptr1, Immediate::U32(0), context.current_span()); - fb.ins() - .cond_br(not_null, b, &[], a, &[sum0], context.current_span()); - - // blk0 - fb.switch_to_block(a); - fb.ins().ret(Some(result0), context.current_span()); - - // blk1 - fb.switch_to_block(b); - let rows0 = fb.ins().u32(0, context.current_span()); - let cols0 = fb.ins().u32(0, context.current_span()); - let row_size = fb - .ins() - .mul_imm_checked(cols, Immediate::U32(4), context.current_span()); - fb.ins() - .br(c, &[sum0, rows0, cols0], context.current_span()); - - // blk2(sum1, rows1, cols1) - fb.switch_to_block(c); - let has_more_rows = fb.ins().lt(rows1, rows, context.current_span()); - let row_skip = fb - .ins() - .mul_checked(rows1, row_size, context.current_span()); - fb.ins().cond_br( - has_more_rows, - d, - &[sum1, rows1, cols1], - a, - &[sum1], - context.current_span(), - ); - - // blk3(sum3, rows3, cols3) - fb.switch_to_block(d); - let has_more_cols = fb.ins().lt(cols3, cols, context.current_span()); - fb.ins() - .cond_br(has_more_cols, e, &[], f, &[], context.current_span()); - - // blk4 - fb.switch_to_block(e); - let col_skip = fb - .ins() - .mul_imm_checked(cols3, Immediate::U32(4), context.current_span()); - let skip = fb - .ins() - .add_checked(row_skip, col_skip, context.current_span()); - let ptr4i = fb.ins().add_checked(ptr1, skip, context.current_span()); - let ptr4 = fb.ins().inttoptr( - ptr4i, - Type::Ptr(Box::new(Type::U32)), - context.current_span(), - ); - let value = fb.ins().load(ptr4, context.current_span()); - let sum4 = fb.ins().add_checked(sum3, value, context.current_span()); - let cols4 = fb.ins().incr_wrapping(cols3, context.current_span()); - fb.ins() - .br(d, &[sum4, rows3, cols4], context.current_span()); - - // blk5 - fb.switch_to_block(f); - let rows5 = fb.ins().incr_wrapping(rows3, context.current_span()); - let cols5 = fb.ins().u32(0, context.current_span()); - fb.ins() - .br(c, &[sum3, rows5, cols5], context.current_span()); - - // We're done - fb.build(&context.session.diagnostics) - .expect("unexpected validation error, see diagnostics output") -} - -/// Add a predefined set of intrinsics to a given [ProgramBuilder], making -/// them available for use by other modules in that program. -/// -/// This defines the following modules and functions: -/// -/// * The `mem` module, containing memory-management intrinsics, -/// see [mem_intrinsics] for details. -/// * The `str` module, containing string-related intrinsics, -/// see [str_intrinsics] for details -pub fn intrinsics(builder: &mut ProgramBuilder, context: &TestContext) -> anyhow::Result<()> { - mem_intrinsics(builder, context)?; - str_intrinsics(builder, context) -} - -/// Adds a `mem` module to the given [ProgramBuilder], containing a handful of -/// useful memory-management intrinsics: -/// -/// * `malloc(u32) -> *mut u8`, allocates from the usable heap -/// * `memory_size() -> u32`, returns the amount of allocated memory, in pages -/// * `memory_grow(u32) -> u32`, grows memory by a given number of pages, -/// returning the previous memory size -/// -/// Expressed as pseudocode, the `mem` module is as follows: -/// -/// ```text,ignore -/// module mem -/// -/// /// These three globals are provided by the linker, based on where the unreserved -/// /// heap memory segment begins and ends. -/// extern { -/// #[no_mangle] -/// static mut HEAP_BASE: *mut u8; -/// #[no_mangle] -/// static mut HEAP_END: *mut u8; -/// #[no_mangle] -/// static mut HEAP_TOP: *mut u8; -/// } -/// -/// pub const PAGE_SIZE: usize = 64 * 1024; -/// -/// /// Allocate `size` bytes from the memory reserved for heap allocations -/// pub fn malloc(size: usize) -> *mut u8 { -/// let top = HEAP_TOP as usize; -/// let available = (HEAP_END as usize - top); -/// if size > available { -/// let needed = size - available; -/// let pages = (needed / PAGE_SIZE) + ((needed % PAGE_SIZE) > 0) as usize; -/// assert_ne!(memory_grow(pages), usize::MAX); -/// } -/// let addr = top + size; -/// let mut ptr: *mut u8 = addr as *mut u8; -/// // Require a min alignment of 8 bytes -/// let align_offset = addr % 8; -/// if align_offset != 0 { -/// ptr = (addr + (8 - align_offset)) as *mut u8; -/// } -/// HEAP_TOP = ptr; -/// ptr -/// } -/// -/// /// Get the size, in pages, of the current heap -/// pub fn memory_size() -> usize { -/// (HEAP_END as usize - HEAP_BASE as usize) / PAGE_SIZE -/// } -/// -/// /// Grow the number of pages reserved for the heap by `num_pages`, returning the previous page count -/// /// -/// /// Returns `usize::MAX` if unable to allocate the requested number of pages -/// pub fn memory_grow(num_pages: usize) -> usize { -/// const HEAP_MAX: usize = 2u32.pow(30); -/// let end = HEAP_END as usize; -/// let remaining = (HEAP_MAX - end) / PAGE_SIZE; -/// if num_pages > remaining { -/// usize::MAX -/// } else { -/// let prev = (end - HEAP_BASE as usize) / PAGE_SIZE; -/// HEAP_END = (end + (num_pages * PAGE_SIZE)) as *mut u8; -/// prev -/// } -/// } -/// ``` -pub fn mem_intrinsics(builder: &mut ProgramBuilder, _context: &TestContext) -> anyhow::Result<()> { - // Next up, the `mem` module - let mut mb = builder.module("mem"); - - // This module knows about the stack segment, but no others - mb.declare_data_segment(0, PAGE_SIZE, vec![], false) - .expect("unexpected data segment error"); - - // pub const PAGE_SIZE: usize = 64 * 1024; - mb.declare_global_variable( - "PAGE_SIZE", - Type::U32, - Linkage::External, - Some(PAGE_SIZE.to_le_bytes().into()), - SourceSpan::UNKNOWN, - ) - .expect("unexpected global variable error"); - - // Define the alloc function - let mut fb = mb - .function("alloc", malloc_signature()) - .expect("unexpected symbol conflict"); - - let memory_grow_sig = Signature::new([AbiParam::new(Type::U32)], [AbiParam::new(Type::U32)]); - let memory_grow = fb - .import_function("mem", "memory_grow", memory_grow_sig.clone()) - .unwrap(); - - // pub fn alloc(size: usize) -> *mut u8 { - // let top = HEAP_TOP as usize; - // let available = (HEAP_END as usize - top); - // if size > available { - // let needed = size - available; - // let pages = (needed / PAGE_SIZE) + ((needed % PAGE_SIZE) > 0) as usize; - // assert_ne!(memory_grow(pages), usize::MAX); - // } - // let addr = top + size; - // let mut ptr: *mut u8 = addr as *mut u8; - // // Require a min alignment of 8 bytes - // let align_offset = addr % 8; - // if align_offset != 0 { - // ptr = (addr + (8 - align_offset)) as *mut u8; - // } - // HEAP_TOP = ptr; - // ptr - // } - let raw_ptr_ty = Type::Ptr(Box::new(Type::U8)); - let size = { - let args = fb.block_params(fb.current_block()); - args[0] - }; - let heap_top = fb - .ins() - .load_symbol("HEAP_TOP", Type::U32, SourceSpan::UNKNOWN); - let heap_end = fb - .ins() - .load_symbol("HEAP_END", Type::U32, SourceSpan::UNKNOWN); - let available = fb - .ins() - .sub_checked(heap_end, heap_top, SourceSpan::UNKNOWN); - let requires_growth = fb.ins().gt(size, available, SourceSpan::UNKNOWN); - let grow_mem_block = fb.create_block(); - let alloc_block = fb.create_block(); - fb.ins().cond_br( - requires_growth, - grow_mem_block, - &[], - alloc_block, - &[], - SourceSpan::UNKNOWN, - ); - - fb.switch_to_block(grow_mem_block); - let needed = fb.ins().sub_checked(size, available, SourceSpan::UNKNOWN); - let need_pages = - fb.ins() - .div_imm_checked(needed, Immediate::U32(PAGE_SIZE), SourceSpan::UNKNOWN); - let need_extra = - fb.ins() - .mod_imm_checked(needed, Immediate::U32(PAGE_SIZE), SourceSpan::UNKNOWN); - let extra_page = fb - .ins() - .gt_imm(need_extra, Immediate::U32(0), SourceSpan::UNKNOWN); - let extra_count = fb.ins().zext(extra_page, Type::U32, SourceSpan::UNKNOWN); - let num_pages = fb - .ins() - .add_checked(need_pages, extra_count, SourceSpan::UNKNOWN); - let prev_pages = { - let call = fb - .ins() - .call(memory_grow, &[num_pages], SourceSpan::UNKNOWN); - fb.first_result(call) - }; - let usize_max = fb.ins().u32(u32::MAX, SourceSpan::UNKNOWN); - fb.ins() - .assert_eq(prev_pages, usize_max, SourceSpan::UNKNOWN); - fb.ins().br(alloc_block, &[], SourceSpan::UNKNOWN); - - fb.switch_to_block(alloc_block); - let addr = fb.ins().add_checked(heap_top, size, SourceSpan::UNKNOWN); - let align_offset = fb - .ins() - .mod_imm_checked(addr, Immediate::U32(8), SourceSpan::UNKNOWN); - let is_aligned = fb - .ins() - .eq_imm(align_offset, Immediate::U32(0), SourceSpan::UNKNOWN); - let align_block = fb.create_block(); - let aligned_block = fb.create_block(); - let new_heap_top_ptr = - fb.append_block_param(aligned_block, raw_ptr_ty.clone(), SourceSpan::UNKNOWN); - - let ptr = fb - .ins() - .inttoptr(addr, raw_ptr_ty.clone(), SourceSpan::UNKNOWN); - fb.ins().cond_br( - is_aligned, - aligned_block, - &[ptr], - align_block, - &[], - SourceSpan::UNKNOWN, - ); - - fb.switch_to_block(align_block); - let aligned_addr = fb - .ins() - .add_imm_checked(addr, Immediate::U32(8), SourceSpan::UNKNOWN); - let aligned_addr = fb - .ins() - .sub_checked(aligned_addr, align_offset, SourceSpan::UNKNOWN); - let aligned_ptr = fb - .ins() - .inttoptr(aligned_addr, raw_ptr_ty.clone(), SourceSpan::UNKNOWN); - fb.ins() - .br(aligned_block, &[aligned_ptr], SourceSpan::UNKNOWN); - - fb.switch_to_block(aligned_block); - let heap_top_addr = fb.ins().symbol_addr( - "HEAP_TOP", - Type::Ptr(Box::new(raw_ptr_ty.clone())), - SourceSpan::UNKNOWN, - ); - fb.ins() - .store(heap_top_addr, new_heap_top_ptr, SourceSpan::UNKNOWN); - fb.ins().ret(Some(new_heap_top_ptr), SourceSpan::UNKNOWN); - - let _alloc = fb - .build() - .expect("unexpected validation error, see diagnostics output"); - - // Define the memory_size function - let memory_size_sig = Signature::new([], [AbiParam::new(Type::U32)]); - let mut fb = mb - .function("memory_size", memory_size_sig) - .expect("unexpected symbol conflict"); - - // pub fn memory_size() -> usize { - // (HEAP_END as usize - HEAP_BASE as usize) / PAGE_SIZE - // } - let heap_base_addr = fb - .ins() - .load_symbol("HEAP_BASE", Type::U32, SourceSpan::UNKNOWN); - let heap_end_addr = fb - .ins() - .load_symbol("HEAP_END", Type::U32, SourceSpan::UNKNOWN); - let used = fb - .ins() - .sub_checked(heap_end_addr, heap_base_addr, SourceSpan::UNKNOWN); - let used_pages = fb - .ins() - .div_imm_checked(used, Immediate::U32(PAGE_SIZE), SourceSpan::UNKNOWN); - fb.ins().ret(Some(used_pages), SourceSpan::UNKNOWN); - - let _memory_size = fb - .build() - .expect("unexpected validation error, see diagnostics output"); - - // Define the memory_grow function - let mut fb = mb - .function("memory_grow", memory_grow_sig) - .expect("unexpected symbol conflict"); - - // pub fn memory_grow(num_pages: usize) -> usize { - // const HEAP_MAX: usize = 2u32.pow(30); - // let end = HEAP_END as usize; - // let remaining = (HEAP_MAX - end) / PAGE_SIZE; - // if num_pages > remaining { - // usize::MAX - // } else { - // let prev = (end - HEAP_BASE as usize) / PAGE_SIZE; - // HEAP_END = (end + (num_pages * PAGE_SIZE)) as *mut u8; - // prev - // } - // } - let num_pages = { - let args = fb.block_params(fb.current_block()); - args[0] - }; - let heap_end = fb - .ins() - .load_symbol("HEAP_END", Type::U32, SourceSpan::UNKNOWN); - let heap_max = fb.ins().u32(u32::MAX, SourceSpan::UNKNOWN); - let remaining_bytes = fb - .ins() - .sub_checked(heap_max, heap_end, SourceSpan::UNKNOWN); - let remaining_pages = fb.ins().div_imm_checked( - remaining_bytes, - Immediate::U32(PAGE_SIZE), - SourceSpan::UNKNOWN, - ); - let out_of_memory = fb.ins().gt(num_pages, remaining_pages, SourceSpan::UNKNOWN); - let out_of_memory_block = fb.create_block(); - let grow_memory_block = fb.create_block(); - fb.ins().cond_br( - out_of_memory, - out_of_memory_block, - &[], - grow_memory_block, - &[], - SourceSpan::UNKNOWN, - ); - - fb.switch_to_block(out_of_memory_block); - fb.ins() - .ret_imm(Immediate::U32(u32::MAX), SourceSpan::UNKNOWN); - - fb.switch_to_block(grow_memory_block); - let heap_base = fb - .ins() - .load_symbol("HEAP_BASE", Type::U32, SourceSpan::UNKNOWN); - let prev_bytes = fb - .ins() - .sub_checked(heap_end, heap_base, SourceSpan::UNKNOWN); - let prev_pages = - fb.ins() - .div_imm_checked(prev_bytes, Immediate::U32(PAGE_SIZE), SourceSpan::UNKNOWN); - let num_bytes = - fb.ins() - .mul_imm_checked(num_pages, Immediate::U32(PAGE_SIZE), SourceSpan::UNKNOWN); - let new_heap_end = fb - .ins() - .add_checked(heap_end, num_bytes, SourceSpan::UNKNOWN); - let heap_end_addr = fb.ins().symbol_addr( - "HEAP_END", - Type::Ptr(Box::new(Type::U32)), - SourceSpan::UNKNOWN, - ); - fb.ins() - .store(heap_end_addr, new_heap_end, SourceSpan::UNKNOWN); - fb.ins().ret(Some(prev_pages), SourceSpan::UNKNOWN); - - let _memory_grow = fb - .build() - .expect("unexpected validation error, see diagnostics output"); - - mb.build()?; - - Ok(()) -} - -/// Adds a `str` module to the given [ProgramBuilder], containing a handful of -/// useful string-related intrinsics: -/// -/// * `from_raw_parts(*mut u8, u32)`, gets a &str from a pointer + length -/// * `compare(&str, &str) -> i8`, compares two strings, returning -1, 0, or 1 -/// -/// Expressed as pseudocode, the `str` module is as follows: -/// -/// ```text,ignore -/// module str -/// -/// // This is to illustrate the type layout of a string reference -/// type &str = { *mut u8, usize }; -/// -/// /// Convert a raw pointer and length to a `&str`, assert the pointer is non-null -/// pub fn from_raw_parts(ptr: *mut u8, len: usize) -> &str { -/// assert!(ptr as usize); -/// // This intrinsic represents construction of the fat pointer for the &str reference -/// let ptr = intrinsics::ptr::from_raw_parts(ptr as *mut (), len); -/// &*ptr -/// } -/// -/// /// Compare two `&str`, returning one of the following: -/// /// -/// /// * 1 if `a` is greater than `b` -/// /// * 0 if `a` is equal to `b` -/// /// * -1 if `a` is less than `b` -/// pub fn compare(a: &str, b: &str) -> i8 { -/// let len = max(a.len, b.len); -/// let mut i = 0; -/// while i < len { -/// let a_char = a.as_bytes()[i]; -/// let b_char = b.as_bytes()[i]; -/// if a_char > b_char { -/// return 1; -/// } else if a_char < b_char { -/// return -1; -/// } else { -/// i += 1; -/// } -/// } -/// -/// if a.len > b.len { -/// 1 -/// } else if a.len < b.len { -/// -1 -/// } else { -/// 0 -/// } -/// } -/// ``` -pub fn str_intrinsics(builder: &mut ProgramBuilder, _context: &TestContext) -> anyhow::Result<()> { - // Next up, the `str` module - let mut mb = builder.module("str"); - - // Define the from_raw_parts function - let mut fb = mb - .function("from_raw_parts", str_from_raw_parts_signature()) - .expect("unexpected symbol conflict"); - - // Unlike the high-level pseudocode, the actual implementation uses an sret parameter, i.e.: - // - // pub fn from_raw_parts(sret result: *mut str, ptr: *mut u8, len: usize) { - // assert!(ptr as usize); - // *result = { ptr, len }; - // } - let (result, ptr, len) = { - let args = fb.block_params(fb.current_block()); - (args[0], args[1], args[2]) - }; - let addr = fb.ins().ptrtoint(ptr, Type::U32, SourceSpan::UNKNOWN); - let is_nonnull_addr = fb - .ins() - .gt_imm(addr, Immediate::U32(0), SourceSpan::UNKNOWN); - fb.ins().assert(is_nonnull_addr, SourceSpan::UNKNOWN); - let ptr_ptr = fb.ins().getelementptr(result, &[0], SourceSpan::UNKNOWN); - fb.ins().store(ptr_ptr, ptr, SourceSpan::UNKNOWN); - let len_ptr = fb.ins().getelementptr(result, &[1], SourceSpan::UNKNOWN); - fb.ins().store(len_ptr, len, SourceSpan::UNKNOWN); - fb.ins().ret(None, SourceSpan::UNKNOWN); - - let _from_raw_parts = fb - .build() - .expect("unexpected validation error, see diagnostics output"); - - // Define the compare function - let mut fb = mb - .function("compare", str_compare_signature()) - .expect("unexpected symbol conflict"); - - // pub fn compare(a: &str, b: &str) -> i8 { - // let len = max(a.len, b.len); - // let mut i = 0; - // while i < len { - // let a_char = a.as_bytes()[i]; - // let b_char = b.as_bytes()[i]; - // if a_char > b_char { - // return 1; - // } else if a_char < b_char { - // return -1; - // } else { - // i += 1; - // } - // } - // - // if a.len > b.len { - // 1 - // } else if a.len < b.len { - // -1 - // } else { - // 0 - // } - // } - let (a, b) = { - let args = fb.block_params(fb.current_block()); - (args[0], args[1]) - }; - let a_ptr_ptr = fb.ins().getelementptr(a, &[0], SourceSpan::UNKNOWN); - let a_ptr = fb.ins().load(a_ptr_ptr, SourceSpan::UNKNOWN); - let a_addr = fb.ins().ptrtoint(a_ptr, Type::U32, SourceSpan::UNKNOWN); - let b_ptr_ptr = fb.ins().getelementptr(b, &[0], SourceSpan::UNKNOWN); - let b_ptr = fb.ins().load(b_ptr_ptr, SourceSpan::UNKNOWN); - let b_addr = fb.ins().ptrtoint(b_ptr, Type::U32, SourceSpan::UNKNOWN); - - let a_len_ptr = fb.ins().getelementptr(a, &[1], SourceSpan::UNKNOWN); - let a_len = fb.ins().load(a_len_ptr, SourceSpan::UNKNOWN); - let b_len_ptr = fb.ins().getelementptr(b, &[1], SourceSpan::UNKNOWN); - let b_len = fb.ins().load(b_len_ptr, SourceSpan::UNKNOWN); - let len = fb.ins().max(a_len, b_len, SourceSpan::UNKNOWN); - - let loop_header = fb.create_block(); - let i = fb.append_block_param(loop_header, Type::U32, SourceSpan::UNKNOWN); - let loop_body = fb.create_block(); - let loop_exit = fb.create_block(); - let exit_block = fb.create_block(); - let result = fb.append_block_param(exit_block, Type::I8, SourceSpan::UNKNOWN); - let zero = fb.ins().u32(0, SourceSpan::UNKNOWN); - fb.ins().br(loop_header, &[zero], SourceSpan::UNKNOWN); - - fb.switch_to_block(loop_header); - let done = fb.ins().lt(i, len, SourceSpan::UNKNOWN); - fb.ins() - .cond_br(done, loop_exit, &[], loop_body, &[], SourceSpan::UNKNOWN); - - fb.switch_to_block(loop_body); - let a_char_addr = fb.ins().incr_wrapping(a_addr, SourceSpan::UNKNOWN); - let a_char_ptr = fb.ins().inttoptr( - a_char_addr, - Type::Ptr(Box::new(Type::U8)), - SourceSpan::UNKNOWN, - ); - let a_char = fb.ins().load(a_char_ptr, SourceSpan::UNKNOWN); - let b_char_addr = fb.ins().incr_wrapping(b_addr, SourceSpan::UNKNOWN); - let b_char_ptr = fb.ins().inttoptr( - b_char_addr, - Type::Ptr(Box::new(Type::U8)), - SourceSpan::UNKNOWN, - ); - let b_char = fb.ins().load(b_char_ptr, SourceSpan::UNKNOWN); - let is_eq = fb.ins().eq(a_char, b_char, SourceSpan::UNKNOWN); - let is_gt = fb.ins().gt(a_char, b_char, SourceSpan::UNKNOWN); - let zero = fb.ins().i8(0, SourceSpan::UNKNOWN); - let one = fb.ins().i8(1, SourceSpan::UNKNOWN); - let neg_one = fb.ins().i8(-1, SourceSpan::UNKNOWN); - let is_ne_result = fb.ins().select(is_gt, one, neg_one, SourceSpan::UNKNOWN); - let i_incr = fb.ins().incr_wrapping(i, SourceSpan::UNKNOWN); - fb.ins().cond_br( - is_eq, - loop_header, - &[i_incr], - exit_block, - &[is_ne_result], - SourceSpan::UNKNOWN, - ); - - fb.switch_to_block(loop_exit); - let is_len_eq = fb.ins().eq(a_len, b_len, SourceSpan::UNKNOWN); - let is_len_gt = fb.ins().gt(a_len, b_len, SourceSpan::UNKNOWN); - let len_gt_result = fb - .ins() - .select(is_len_gt, one, neg_one, SourceSpan::UNKNOWN); - let len_eq_result = fb - .ins() - .select(is_len_eq, zero, len_gt_result, SourceSpan::UNKNOWN); - fb.ins() - .br(exit_block, &[len_eq_result], SourceSpan::UNKNOWN); - - fb.switch_to_block(exit_block); - fb.ins().ret(Some(result), SourceSpan::UNKNOWN); - - let _compare = fb - .build() - .expect("unexpected validation error, see diagnostics output"); - - mb.build()?; - - Ok(()) -} - -/// This function uses the provided [ProgramBuilder] to define a module which exercises -/// a variety of fundamental functionality in the IR, and in Miden Assembly: -/// -/// * Memory access, management -/// * Global variables, data segments -/// * Function calls -/// * Assertions -/// -/// NOTE: This module builds on the [intrinsics] helper. -/// -/// The following is pseudocode representing the module we define and the program entrypoint. -/// -/// ```text,ignore -/// module test -/// -/// use mem; -/// use str; -/// -/// /// This is here solely to ensure that globals are linked correctly -/// extern { -/// const PAGE_SIZE: usize; -/// } -/// -/// pub fn main() -> i32 { -/// const HELLO: &str = "hello"; -/// -/// let len = HELLO.as_bytes().len(); -/// let ptr: *mut u8 = mem::alloc(len); -/// memcpy(HELLO.as_bytes().as_ptr(), ptr, len); -/// let greeting = str::from_raw_parts(ptr, len); -/// -/// assert_eq!(PAGE_SIZE, 64 * 1024); -/// assertz!(str::compare(HELLO, greeting)); -/// -/// 0 -/// } -/// ``` -pub fn hello_world(builder: &mut ProgramBuilder, context: &TestContext) -> anyhow::Result<()> { - let mut mb = builder.module("test"); - - // Every module is going to have the same data segment for the shadow stack, - // and this module will additionally have a data segment for read-only data, - // i.e. constants - mb.declare_data_segment(0, PAGE_SIZE, vec![], false) - .expect("unexpected data segment error"); - mb.declare_data_segment(PAGE_SIZE, PAGE_SIZE, b"hello\0".to_vec(), true) - .expect("unexpected data segment error"); - - // Declare the `main` function, with the appropriate type signature - let sig = Signature::new([], [AbiParam::new(Type::I32)]); - - let mut fb = mb - .function("main", sig) - .expect("unexpected symbol conflict"); - - let raw_ptr_ty = Type::Ptr(Box::new(Type::U8)); - let malloc = fb - .import_function("mem", "alloc", malloc_signature()) - .unwrap(); - let str_from_raw_parts = fb - .import_function("str", "from_raw_parts", str_from_raw_parts_signature()) - .unwrap(); - let str_compare = fb - .import_function("str", "compare", str_compare_signature()) - .unwrap(); - - // const HELLO: &str = "hello"; - // - // let len = HELLO.as_bytes().len(); - // let ptr: *mut u8 = mem::alloc(len); - // memcpy(HELLO.as_bytes().as_ptr(), ptr, len); - // let greeting = str::from_raw_parts(ptr, len); - // - // assert_eq!(PAGE_SIZE, 64 * 1024); - // assertz!(str::compare(HELLO, greeting)); - // - // 0 - let hello_data = [PAGE_SIZE, 5u32]; - let hello_data = unsafe { - let slice = hello_data.as_slice(); - ConstantData::from(slice::from_raw_parts( - slice.as_ptr() as *const u8, - mem::size_of::() * 2, - )) - }; - fb.module() - .declare_global_variable( - "HELLO", - str_type(), - Linkage::Odr, - Some(hello_data), - SourceSpan::UNKNOWN, - ) - .expect("unexpected global variable error"); - let len = fb.ins().load_symbol_relative( - "HELLO", - Type::U32, - mem::size_of::() as i32, - SourceSpan::UNKNOWN, - ); - let ptr = { - let call = fb.ins().call(malloc, &[len], SourceSpan::UNKNOWN); - fb.first_result(call) - }; - let hello_gv = fb.ins().symbol("HELLO", SourceSpan::UNKNOWN); - let hello_data_ptr = fb.ins().load_global_relative( - hello_gv, - raw_ptr_ty.clone(), - mem::size_of::() as i32, - SourceSpan::UNKNOWN, - ); - //let hello_data_ptr = fb.ins().load_symbol_relative("HELLO", raw_ptr_ty.clone(), mem::size_of::(), SourceSpan::UNKNOWN); - fb.ins() - .memcpy(hello_data_ptr, ptr, len, SourceSpan::UNKNOWN); - let greeting_ptr = fb.ins().alloca(str_type(), SourceSpan::UNKNOWN); - fb.ins().call( - str_from_raw_parts, - &[greeting_ptr, ptr, len], - SourceSpan::UNKNOWN, - ); - let page_size = fb - .ins() - .load_symbol("PAGE_SIZE", Type::U32, SourceSpan::UNKNOWN); - let expected_page_size = fb.ins().u32(PAGE_SIZE, SourceSpan::UNKNOWN); - fb.ins() - .assert_eq(page_size, expected_page_size, SourceSpan::UNKNOWN); - let compared = { - let hello_ptr = fb.ins().symbol_addr( - "HELLO", - Type::Ptr(Box::new(str_type())), - SourceSpan::UNKNOWN, - ); - let call = fb - .ins() - .call(str_compare, &[hello_ptr, greeting_ptr], SourceSpan::UNKNOWN); - fb.first_result(call) - }; - let compared = fb.ins().trunc(compared, Type::I1, SourceSpan::UNKNOWN); - fb.ins().assertz(compared, SourceSpan::UNKNOWN); - fb.ins().ret_imm(Immediate::I32(0), SourceSpan::UNKNOWN); - - // Finalize 'test::main' - fb.build() - .expect("unexpected validation error, see diagnostics output"); - mb.build()?; - - // Add intrinsics - intrinsics(builder, context) -} - -#[inline] -fn str_type() -> Type { - Type::Struct(StructType::new([Type::Ptr(Box::new(Type::U8)), Type::U32])) -} - -fn malloc_signature() -> Signature { - Signature::new( - [AbiParam::new(Type::U32)], - [AbiParam::new(Type::Ptr(Box::new(Type::U8)))], - ) -} - -fn str_from_raw_parts_signature() -> Signature { - Signature::new( - [ - AbiParam::sret(Type::Ptr(Box::new(str_type()))), - AbiParam::new(Type::Ptr(Box::new(Type::U8))), - AbiParam::new(Type::U32), - ], - [], - ) -} - -fn str_compare_signature() -> Signature { - let str_ptr_ty = Type::Ptr(Box::new(str_type())); - let a = AbiParam::new(str_ptr_ty); - let b = a.clone(); - Signature::new([a, b], [AbiParam::new(Type::I8)]) -} diff --git a/hir/src/tests.rs b/hir/src/tests.rs deleted file mode 100644 index d6576a298..000000000 --- a/hir/src/tests.rs +++ /dev/null @@ -1,156 +0,0 @@ -use winter_math::FieldElement; - -use super::{ - testing::{self, TestContext}, - *, -}; - -/// Test that we can construct a basic module and function and validate it -#[test] -fn simple_builder_test() { - let context = TestContext::default(); - - let mut builder = ProgramBuilder::new(&context.session.diagnostics); - { - let mut mb = builder.module("test"); - testing::fib1(mb.as_mut(), &context); - mb.build().expect("unexpected error building test module"); - } - builder.link().expect("failed to link program"); -} - -/// Test that we can emit inline assembly within a function, and correctly validate it -#[test] -fn inline_asm_builders_test() { - let context = TestContext::default(); - - // Define the 'test' module - let mut builder = ModuleBuilder::new("test"); - - // Declare the `sum` function, with the appropriate type signature - let sig = Signature { - params: vec![ - AbiParam::new(Type::Ptr(Box::new(Type::Felt))), - AbiParam::new(Type::U32), - ], - results: vec![AbiParam::new(Type::Felt)], - cc: CallConv::SystemV, - linkage: Linkage::External, - }; - let mut fb = builder - .function("sum", sig) - .expect("unexpected symbol conflict"); - - let entry = fb.current_block(); - let (ptr, len) = { - let args = fb.block_params(entry); - (args[0], args[1]) - }; - - 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] - - // 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(); - - // Calculate `i / 4` - lb.ins().dup(0); // [i, i, sum, ptr, len] - lb.ins().div_imm_u32(4); // [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] - - // 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, ..] - - // 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] - 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, ..] - - // 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] - - // 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] - - // 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] - - // Finalize, it is at this point that validation will occur - lb.build(); - - // 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(); - rb.build(); // [sum] - - // Finish the inline assembly block - let asm = asm_builder.build(); - // Extract the result from the inline assembly block - let sum = fb.data_flow_graph().first_result(asm); - fb.ins().ret(Some(sum), SourceSpan::default()); - - // Finish building the function, getting back the function identifier - let _sum = fb - .build(&context.session.diagnostics) - .expect("unexpected validation error, see diagnostics output"); - - // Finalize the module - builder.build(); -} - -/// Test that we can construct and link a set of modules correctly -#[test] -fn linker_test() { - let context = TestContext::default(); - - let mut builder = ProgramBuilder::new(&context.session.diagnostics); - testing::hello_world(&mut builder, &context) - .expect("unexpected error constructing test modules"); - - let _program = builder - .with_entrypoint("test::main".parse().unwrap()) - .link() - .expect("failed to link program"); -} diff --git a/hir/src/value.rs b/hir/src/value.rs deleted file mode 100644 index 0436bca3a..000000000 --- a/hir/src/value.rs +++ /dev/null @@ -1,108 +0,0 @@ -use cranelift_entity::{self as entity, entity_impl}; - -use miden_diagnostics::SourceSpan; - -use super::{Block, Inst, Type}; - -pub type ValueList = entity::EntityList; -pub type ValueListPool = entity::ListPool; - -/// A handle to a single SSA value -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Value(u32); -entity_impl!(Value, "v"); -impl Default for Value { - #[inline] - fn default() -> Self { - use cranelift_entity::packed_option::ReservedValue; - - Self::reserved_value() - } -} - -/// Data associated with a `Value`. -/// -/// Values are either block arguments, instructions or aliases, and -/// in addition to being linked to a `Inst` or a `Block`, they -/// have an associated type, position, and in some cases, a `SourceSpan`. -#[derive(Debug, Clone)] -pub enum ValueData { - Inst { - ty: Type, - num: u16, - inst: Inst, - }, - Param { - ty: Type, - num: u16, - block: Block, - span: SourceSpan, - }, -} -impl ValueData { - pub fn ty(&self) -> &Type { - match self { - Self::Inst { ref ty, .. } | Self::Param { ref ty, .. } => ty, - } - } - - pub fn span(&self) -> SourceSpan { - match self { - Self::Inst { .. } => SourceSpan::UNKNOWN, - Self::Param { span, .. } => *span, - } - } - - /// Update the block to which a block parameter belongs - /// - /// NOTE: This function will panic if the value is not a block parameter - /// - /// # Safety - /// - /// This function is marked unsafe because changing the block associated - /// with a value could cause unexpected results if the other pieces of the - /// DataFlowGraph are not updated correctly. Callers must ensure that this - /// is _only_ called when a block parameter has been moved to another block. - pub unsafe fn set_block(&mut self, block: Block) { - match self { - Self::Param { - block: ref mut orig, - .. - } => { - *orig = block; - } - _ => panic!("expected block parameter, got instruction result"), - } - } - - pub fn set_type(&mut self, ty: Type) { - match self { - Self::Inst { - ty: ref mut prev_ty, - .. - } => *prev_ty = ty, - Self::Param { - ty: ref mut prev_ty, - .. - } => *prev_ty = ty, - } - } - - pub fn unwrap_inst(&self) -> Inst { - match self { - Self::Inst { inst, .. } => *inst, - _ => panic!("expected instruction result value, got block parameter"), - } - } -} - -pub struct Values<'a> { - pub(super) inner: entity::Iter<'a, Value, ValueData>, -} -impl<'a> Iterator for Values<'a> { - type Item = Value; - - fn next(&mut self) -> Option { - self.inner.by_ref().next().map(|kv| kv.0) - } -} diff --git a/hir/src/write.rs b/hir/src/write.rs deleted file mode 100644 index f4a8feb3b..000000000 --- a/hir/src/write.rs +++ /dev/null @@ -1,425 +0,0 @@ -use std::fmt::{self, Write}; - -use super::{display::DisplayValues, *}; - -pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result { - for attr in func.dfg.attrs.iter() { - writeln!(w, "{attr}")?; - } - write_signature(w, None, func.id.function, &func.signature)?; - writeln!(w, " {{")?; - for (i, (block, block_data)) in func.dfg.blocks().enumerate() { - if i > 0 { - writeln!(w)?; - } - - write_block_header(w, func, block, 4)?; - for inst in block_data.insts() { - write_instruction(w, func, inst, 4)?; - } - } - writeln!(w, "}}") -} - -pub fn write_external_function( - w: &mut dyn Write, - name: &FunctionIdent, - signature: &Signature, -) -> fmt::Result { - write_signature(w, Some(name.module), name.function, signature)?; - writeln!(w, ";") -} - -fn write_signature( - w: &mut dyn Write, - module: Option, - name: Ident, - signature: &Signature, -) -> fmt::Result { - if signature.is_public() { - write!(w, "pub ")?; - } - match signature.cc { - CallConv::Fast => w.write_str("cc(fast) fn ")?, - CallConv::SystemV => w.write_str("fn ")?, - CallConv::Kernel => w.write_str("cc(kernel) fn ")?, - } - match module { - None => write!(w, "{}(", DisplayIdent(&name))?, - Some(module) => write!( - w, - "{}(", - &FunctionIdent { - module, - function: name - } - )?, - } - for (i, param) in signature.params().iter().enumerate() { - if i > 0 { - w.write_str(", ")?; - } - match param.purpose { - ArgumentPurpose::Default => (), - purpose => write!(w, "{} ", purpose)?, - } - match param.extension { - ArgumentExtension::None => (), - ext => write!(w, "{} ", ext)?, - } - write!(w, "{}", ¶m.ty)?; - } - w.write_str(")")?; - let results = signature.results(); - if !results.is_empty() { - w.write_str(" -> ")?; - for (i, result) in results.iter().enumerate() { - if i > 0 { - w.write_str(", ")?; - } - match result.extension { - ArgumentExtension::None => (), - ArgumentExtension::Zext => w.write_str("zext ")?, - ArgumentExtension::Sext => w.write_str("sext ")?, - } - write!(w, "{}", &result.ty)?; - } - } - - Ok(()) -} - -fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result { - write!(w, "{}: {}", arg, func.dfg.value_type(arg)) -} - -pub fn write_block_header( - w: &mut dyn Write, - func: &Function, - block: Block, - indent: usize, -) -> fmt::Result { - // The indent is for instructions, block header is 4 spaces outdented - write!(w, "{1:0$}{2}", indent - 4, "", block)?; - - let mut args = func.dfg.block_params(block).iter().cloned(); - match args.next() { - None => return writeln!(w, ":"), - Some(arg) => { - write!(w, "(")?; - write_arg(w, func, arg)?; - } - } - for arg in args { - write!(w, ", ")?; - write_arg(w, func, arg)?; - } - writeln!(w, "):") -} - -pub fn write_instruction( - w: &mut dyn Write, - func: &Function, - inst: Inst, - indent: usize, -) -> fmt::Result { - write_indent(w, indent)?; - - let mut has_results = false; - for r in func.dfg.inst_results(inst) { - if !has_results { - has_results = true; - write!(w, "{}", r)?; - } else { - write!(w, ", {}", r)?; - } - } - if has_results { - write!(w, " = ")? - } - - let opcode = func.dfg[inst].opcode(); - write!(w, "{}", opcode)?; - write_operands(w, &func.id, &func.dfg, inst, indent)?; - - if has_results { - write!(w, " : ")?; - for (i, v) in func.dfg.inst_results(inst).iter().enumerate() { - let t = func.dfg.value_type(*v).to_string(); - if i > 0 { - write!(w, ", {}", t)?; - } else { - write!(w, "{}", t)?; - } - } - } - - writeln!(w, ";")?; - - Ok(()) -} - -fn write_operands( - w: &mut dyn Write, - function: &FunctionIdent, - dfg: &DataFlowGraph, - inst: Inst, - indent: usize, -) -> fmt::Result { - let pool = &dfg.value_lists; - match &dfg[inst] { - Instruction::BinaryOp(BinaryOp { - overflow: None, - args, - .. - }) => { - write!(w, " {}, {}", args[1], args[0]) - } - Instruction::BinaryOp(BinaryOp { - overflow: Some(overflow), - args, - .. - }) => { - write!(w, ".{} {}, {}", overflow, args[1], args[0]) - } - Instruction::BinaryOpImm(BinaryOpImm { - overflow: None, - arg, - imm, - .. - }) => write!(w, " {}, {}", arg, imm), - Instruction::BinaryOpImm(BinaryOpImm { - overflow: Some(overflow), - arg, - imm, - .. - }) => write!(w, ".{} {}, {}", overflow, arg, imm), - Instruction::UnaryOp(UnaryOp { - overflow: None, - arg, - .. - }) => write!(w, " {}", arg), - Instruction::UnaryOp(UnaryOp { - overflow: Some(overflow), - arg, - .. - }) => write!(w, ".{} {}", overflow, arg), - Instruction::UnaryOpImm(UnaryOpImm { - overflow: None, - imm, - .. - }) => { - write!(w, " {}", imm) - } - Instruction::UnaryOpImm(UnaryOpImm { - overflow: Some(overflow), - imm, - .. - }) => { - write!(w, ".{} {}", overflow, imm) - } - Instruction::Ret(Ret { args, .. }) => { - if args.len(pool) > 0 { - write!(w, " {}", DisplayValues::new(args.as_slice(pool).iter())) - } else { - Ok(()) - } - } - Instruction::RetImm(RetImm { arg, .. }) => write!(w, " {arg}"), - Instruction::Call(Call { callee, args, .. }) => { - write!( - w, - " {}({})", - callee, - DisplayValues::new(args.as_slice(pool).iter()) - ) - } - Instruction::CondBr(CondBr { - cond, - then_dest, - else_dest, - .. - }) => { - write!(w, " {}, ", cond)?; - write!(w, "{}", then_dest.0)?; - write_block_args(w, then_dest.1.as_slice(pool))?; - write!(w, ", {}", else_dest.0)?; - write_block_args(w, else_dest.1.as_slice(pool)) - } - Instruction::Br(Br { - destination, args, .. - }) => { - write!(w, " {}", destination)?; - write_block_args(w, args.as_slice(pool)) - } - Instruction::Switch(Switch { - arg, arms, default, .. - }) => { - writeln!(w, " {} {{", arg)?; - for (value, dest) in arms.iter() { - write_indent(w, indent + 4)?; - writeln!(w, "{} => {},", value, dest)?; - } - write_indent(w, indent + 4)?; - writeln!(w, "_ => {}", default)?; - write_indent(w, indent)?; - w.write_char('}') - } - Instruction::Test(Test { arg, ref ty, .. }) => { - write!(w, ".{} {}", ty, arg) - } - Instruction::PrimOp(PrimOp { args, .. }) => { - write!(w, " {}", DisplayValues::new(args.as_slice(pool).iter())) - } - Instruction::PrimOpImm(PrimOpImm { imm, args, .. }) => { - write!( - w, - " {}, {}", - imm, - DisplayValues::new(args.as_slice(pool).iter()) - ) - } - Instruction::Load(LoadOp { addr, .. }) => { - write!(w, " {}", addr) - } - Instruction::InlineAsm(ref asm) => { - write!(w, " {}", asm.display(Some(*function), dfg, indent)) - } - Instruction::GlobalValue(GlobalValueOp { global, .. }) => { - write_global_value(w, dfg, *global, false) - } - } -} - -fn write_global_value( - w: &mut dyn Write, - dfg: &DataFlowGraph, - gv: GlobalValue, - nested: bool, -) -> fmt::Result { - match dfg.global_value(gv) { - GlobalValueData::Symbol { name, offset } => { - if !nested { - w.write_str(".symbol ")?; - } - let offset = DisplayOffset::from(*offset); - write!(w, "@{}{}", DisplayIdent(name), offset) - } - GlobalValueData::Load { base, offset, ty } if nested => { - let pointer_ty = dfg.global_type(*base); - let is_cast = pointer_ty.pointee().unwrap() != ty; - let has_offset = *offset != 0; - let offset = DisplayOffset::from(*offset); - if is_cast || has_offset { - write!(w, "*(")?; - } else { - w.write_char('*')?; - } - write_global_value(w, dfg, *base, true)?; - if is_cast { - write!(w, "){} as {}", offset, pointer_ty) - } else if has_offset { - write!(w, "){}", offset) - } else { - Ok(()) - } - } - GlobalValueData::Load { base, offset, ty } => { - let pointer_ty = dfg.global_type(*base); - let is_cast = pointer_ty.pointee().unwrap() != ty; - let has_offset = *offset != 0; - let offset = DisplayOffset::from(*offset); - w.write_str(".load ")?; - if is_cast || has_offset { - w.write_char('(')?; - } - write_global_value(w, dfg, *base, true)?; - if is_cast { - write!(w, "){} as {}", offset, pointer_ty) - } else if has_offset { - write!(w, "){}", offset) - } else { - Ok(()) - } - } - GlobalValueData::IAddImm { base, offset, ty } => { - if !nested { - w.write_char('.')?; - } - write!(w, "iadd.{}.{} ", offset, ty)?; - write_global_value(w, dfg, *base, true) - } - } -} - -fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result { - if args.is_empty() { - Ok(()) - } else { - write!(w, "({})", DisplayValues::new(args.iter())) - } -} - -#[inline(always)] -fn write_indent(w: &mut dyn Write, indent: usize) -> fmt::Result { - write!(w, "{1:0$}", indent, "") -} - -pub(crate) struct DisplayIdent<'a>(pub &'a Ident); -impl<'a> fmt::Display for DisplayIdent<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.0.as_symbol().is_keyword() { - write!(f, "\"{}\"", self.0) - } else { - write!(f, "{}", self.0) - } - } -} - -#[derive(Copy, Clone)] -struct DisplayOffset(i32); -impl From for DisplayOffset { - fn from(offset: i32) -> Self { - Self(offset) - } -} -impl fmt::Display for DisplayOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.0 == 0 { - Ok(()) - } else if self.0 >= 0 { - write!(f, "+{}", self.0) - } else { - write!(f, "{}", self.0) - } - } -} - -pub struct DisplayIndent(pub usize); -impl fmt::Display for DisplayIndent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - const INDENT: &str = " "; - for _ in 0..self.0 { - f.write_str(INDENT)?; - } - Ok(()) - } -} - -pub struct DisplayValuesWithImmediate<'a>(&'a [Value], Immediate); -impl<'a> fmt::Display for DisplayValuesWithImmediate<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (i, val) in self.0.iter().enumerate() { - if i == 0 { - write!(f, "{}", val)?; - } else { - write!(f, ", {}", val)?; - } - } - if self.0.is_empty() { - write!(f, "{}", &self.1) - } else { - write!(f, ", {}", &self.1) - } - } -} diff --git a/midenc-compile/Cargo.toml b/midenc-compile/Cargo.toml deleted file mode 100644 index acbcf05b6..000000000 --- a/midenc-compile/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "midenc-compile" -description = "The compiler frontend for Miden" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -homepage.workspace = true -documentation.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -anyhow.workspace = true -clap.workspace = true -log.workspace = true -inventory.workspace = true -miden-assembly.workspace = true -miden-codegen-masm.workspace = true -miden-diagnostics.workspace = true -miden-frontend-wasm.workspace = true -miden-hir.workspace = true -miden-hir-transform.workspace = true -midenc-session.workspace = true -rustc-hash.workspace = true -thiserror.workspace = true diff --git a/midenc-compile/src/compiler.rs b/midenc-compile/src/compiler.rs deleted file mode 100644 index b7af98639..000000000 --- a/midenc-compile/src/compiler.rs +++ /dev/null @@ -1,164 +0,0 @@ -use std::path::PathBuf; -use std::sync::Arc; - -use clap::{Args, ColorChoice}; -use miden_diagnostics::term::termcolor::ColorChoice as MDColorChoice; -use miden_diagnostics::Emitter; -use midenc_session::{ - InputFile, Options, OutputFile, OutputType, OutputTypeSpec, OutputTypes, ProjectType, Session, - TargetEnv, VerbosityFlag, Warnings, -}; - -/// Compile a program from WebAssembly or Miden IR, to Miden Assembly. -#[derive(Debug, Args)] -pub struct Compiler { - /// The input file to compile - /// - /// You may specify `-` to read from stdin, otherwise you must provide a path - #[arg(required(true), value_name = "FILE")] - input: InputFile, - /// 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_missing_value = "debug", - help_heading = "Diagnostics" - )] - verbosity: VerbosityFlag, - /// 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", - help_heading = "Diagnostics" - )] - 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, - /// The target environment to compile for - #[arg(long, value_name = "TARGET", default_value_t = TargetEnv::Base, help_heading = "Compiler")] - target: TargetEnv, - /// Tells the compiler to produce an executable Miden program - /// - /// When the target is `base` or `rollup`, this defaults to true - #[arg( - long = "exe", - default_value_t = true, - default_value_if("target", "emu", Some("false")), - help_heading = "Compiler" - )] - is_program: bool, - /// Tells the compiler to produce a Miden library - /// - /// When the target is `emu`, this defaults to true - #[arg( - long = "lib", - conflicts_with("is_program"), - default_value_t = false, - default_value_if("target", "emu", Some("true")), - help_heading = "Compiler" - )] - is_library: bool, - /// 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" - )] - 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, - /// Write output to compiler-chosen filename in `` - #[arg( - long, - value_name = "DIR", - env = "MIDENC_OUT_DIR", - help_heading = "Output" - )] - 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, - /// Specify one or more output types for the compiler to emit - #[arg( - long = "emit", - value_name = "SPEC", - value_delimiter = ',', - help_heading = "Output" - )] - output_types: Vec, - /// Print the IR after each pass is applied - #[arg(long, default_value_t = false, help_heading = "Passes")] - 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, -} -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 tmp_dir = self.target_dir.unwrap_or_else(|| std::env::temp_dir()); - - let color = match self.color { - ColorChoice::Auto => MDColorChoice::Auto, - ColorChoice::Always => MDColorChoice::Always, - ColorChoice::Never => MDColorChoice::Never, - }; - - 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::Masl, None); - } - let mut options = Options::new(cwd) - .with_color(color) - .with_verbosity(self.verbosity.into()) - .with_warnings(self.warn) - .with_output_types(output_types); - 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) - } -} diff --git a/midenc-compile/src/lib.rs b/midenc-compile/src/lib.rs deleted file mode 100644 index 282dd75a0..000000000 --- a/midenc-compile/src/lib.rs +++ /dev/null @@ -1,208 +0,0 @@ -mod compiler; -mod stage; -mod stages; - -pub use self::compiler::Compiler; -use self::stage::Stage; -use self::stages::*; - -use std::sync::Arc; - -use miden_codegen_masm as masm; -use miden_hir::{pass::AnalysisManager, Symbol}; -use midenc_session::{OutputType, Session}; - -pub use self::stages::Compiled; - -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 - #[error(transparent)] - WasmError(#[from] miden_frontend_wasm::WasmError), - /// An error occurred while parsing an HIR module - #[error(transparent)] - Parsing(#[from] miden_hir::parser::ParseError), - /// An error occurred while running an analysis - #[error(transparent)] - Analysis(#[from] miden_hir::pass::AnalysisError), - /// An error occurred while rewriting an IR entity - #[error(transparent)] - Rewriting(#[from] miden_hir::pass::RewriteError), - /// An error occurred while converting from one dialect to another - #[error(transparent)] - Conversion(#[from] miden_hir::pass::ConversionError), - /// An error occurred while linking a program - #[error(transparent)] - Linker(#[from] miden_hir::LinkerError), - /// An error occurred while emitting a MASL library - #[error(transparent)] - Masl(#[from] miden_assembly::LibraryError), - /// An error ocurred when reading a file - #[error(transparent)] - Io(#[from] std::io::Error), - /// An error occured 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: miden_hir::ModuleConflictError) -> CompilerError { - Self::Linker(miden_hir::LinkerError::ModuleConflict(err.0)) - } -} - -/// Register dynamic flags to be shown via `midenc help compile` -pub fn register_flags(cmd: clap::Command) -> clap::Command { - use miden_hir::RewritePassRegistration; - use midenc_session::CompileFlag; - - let cmd = inventory::iter:: - .into_iter() - .fold(cmd, |cmd, flag| { - let arg = clap::Arg::new(flag.name) - .long(flag.long.unwrap_or(flag.name)) - .action(clap::ArgAction::from(flag.action)); - let arg = if let Some(help) = flag.help { - arg.help(help) - } else { - arg - }; - let arg = if let Some(help_heading) = flag.help_heading { - arg.help_heading(help_heading) - } else { - arg - }; - let arg = if let Some(short) = flag.short { - arg.short(short) - } else { - arg - }; - let arg = if let Some(env) = flag.env { - arg.env(env) - } else { - arg - }; - let arg = if let Some(value) = flag.default_missing_value { - arg.default_missing_value(value) - } else { - arg - }; - let arg = if let Some(value) = flag.default_value { - arg.default_value(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) - }) -} - -/// 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::Masl, None) { - use miden_assembly::utils::Serializable; - let masl = program.to_masl_library(session.name(), &session.codemap)?; - let bytes = masl.to_bytes(); - std::fs::write(&path, bytes)?; - } - if session.should_emit(OutputType::Masm) { - for module in program.modules() { - session.emit(module)?; - } - if program.is_executable() { - use miden_assembly::LibraryPath; - let ast = program.to_program_ast(&session.codemap); - if let Some(path) = session.emit_to( - OutputType::Masm, - Some(Symbol::intern(LibraryPath::EXEC_PATH)), - ) { - ast.write_to_file(path)?; - } else { - println!("{ast}"); - } - } - } - } - Ok(Compiled::Modules(modules)) => { - let mut program = masm::Program::new(); - for module in modules.into_iter() { - program.insert(module); - } - if let Some(path) = session.emit_to(OutputType::Masl, None) { - use miden_assembly::utils::Serializable; - let masl = program.to_masl_library(session.name(), &session.codemap)?; - let bytes = masl.to_bytes(); - std::fs::write(&path, bytes)?; - } - if session.should_emit(OutputType::Masm) { - for module in program.modules() { - session.emit(module)?; - } - } - } - 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()]; - 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) - } - } -} - -fn compile_inputs( - inputs: Vec, - analyses: &mut AnalysisManager, - session: &Session, -) -> CompilerResult { - let mut stages = ParseStage - .next(SemanticAnalysisStage) - .next_optional(ApplyRewritesStage) - .collect(LinkerStage) - .next(CodegenStage); - - stages.run(inputs, analyses, session) -} diff --git a/midenc-compile/src/stage.rs b/midenc-compile/src/stage.rs deleted file mode 100644 index 4e03f6419..000000000 --- a/midenc-compile/src/stage.rs +++ /dev/null @@ -1,162 +0,0 @@ -use miden_hir::pass::AnalysisManager; -use midenc_session::Session; - -use crate::{CompilerError, CompilerResult}; - -/// This trait is implemented by a stage in the compiler -pub trait Stage { - type Input; - type Output; - - /// Return true if this stage is disabled - fn enabled(&self, _session: &Session) -> bool { - true - } - - /// Run this stage - fn run( - &mut self, - input: Self::Input, - analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult; - - fn next(self, stage: S) -> Chain - where - Self: Sized, - S: Stage, - { - Chain::new(self, stage) - } - - fn next_optional(self, stage: S) -> ChainOptional - where - Self: Sized, - S: Stage, - { - ChainOptional::new(self, stage) - } - - fn collect(self, stage: S) -> Collect - where - Self: Sized, - I: IntoIterator, - S: Stage>, - { - Collect::new(self, stage) - } -} - -/// This struct is used to chain multiple [Stage] together -pub struct Chain { - a: A, - b: B, -} -impl Chain { - fn new(a: A, b: B) -> Self { - Self { a, b } - } -} -impl Stage for Chain -where - A: Stage, - B: Stage::Output>, -{ - type Input = ::Input; - type Output = ::Output; - - fn run<'a>( - &mut self, - input: Self::Input, - analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult { - if !self.a.enabled(session) { - return Err(CompilerError::Stopped); - } - let output = self.a.run(input, analyses, session)?; - if !self.b.enabled(session) { - return Err(CompilerError::Stopped); - } - self.b.run(output, analyses, session) - } -} - -/// This struct is used to chain two [Stages] together when the second might be disabled -pub struct ChainOptional { - a: A, - b: B, -} -impl ChainOptional { - fn new(a: A, b: B) -> Self { - Self { a, b } - } -} -impl Stage for ChainOptional -where - A: Stage, - B: Stage::Output, Output = ::Output>, -{ - type Input = ::Input; - type Output = ::Output; - - fn run<'a>( - &mut self, - input: Self::Input, - analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult { - if !self.a.enabled(session) { - return Err(CompilerError::Stopped); - } - let output = self.a.run(input, analyses, session)?; - if !self.b.enabled(session) { - Ok(output) - } else { - self.b.run(output, analyses, session) - } - } -} - -/// This stage joins multiple inputs into a single output -pub struct Collect { - spread: A, - join: B, - _marker: core::marker::PhantomData, -} -impl Collect -where - A: Stage, - B: Stage::Output>>, - I: IntoIterator::Input>, -{ - pub fn new(spread: A, join: B) -> Self { - Self { - spread, - join, - _marker: core::marker::PhantomData, - } - } -} -impl Stage for Collect -where - A: Stage, - B: Stage::Output>>, - I: IntoIterator::Input>, -{ - type Input = I; - type Output = ::Output; - - fn run( - &mut self, - inputs: Self::Input, - analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult { - let mut outputs = vec![]; - for input in inputs.into_iter() { - outputs.push(self.spread.run(input, analyses, session)?); - } - self.join.run(outputs, analyses, session) - } -} diff --git a/midenc-compile/src/stages/codegen.rs b/midenc-compile/src/stages/codegen.rs deleted file mode 100644 index 6bf66052f..000000000 --- a/midenc-compile/src/stages/codegen.rs +++ /dev/null @@ -1,43 +0,0 @@ -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; - - fn enabled(&self, session: &Session) -> bool { - session.should_codegen() - } - - fn run( - &mut self, - input: Self::Input, - analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult { - match input { - MaybeLinked::Linked(program) => { - let mut convert_to_masm = masm::ConvertHirToMasm::::default(); - let program = convert_to_masm.convert(program, analyses, session)?; - Ok(Compiled::Program(program)) - } - MaybeLinked::Unlinked(modules) => { - let mut convert_to_masm = masm::ConvertHirToMasm::::default(); - let mut masm_modules = Vec::with_capacity(modules.len()); - 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)) - } - } - } -} diff --git a/midenc-compile/src/stages/link.rs b/midenc-compile/src/stages/link.rs deleted file mode 100644 index f6f313437..000000000 --- a/midenc-compile/src/stages/link.rs +++ /dev/null @@ -1,34 +0,0 @@ -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] -pub enum MaybeLinked { - Linked(Box), - Unlinked(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; - - fn run( - &mut self, - input: Self::Input, - _analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult { - 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())) - } - } -} diff --git a/midenc-compile/src/stages/mod.rs b/midenc-compile/src/stages/mod.rs deleted file mode 100644 index f78e431fc..000000000 --- a/midenc-compile/src/stages/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -use super::Stage; -use crate::{CompilerError, CompilerResult}; -use miden_codegen_masm as masm; -use miden_frontend_wasm as wasm; -use miden_hir::pass::{AnalysisManager, ConversionPass, RewritePass}; -use miden_hir::{self as hir, parser::ast}; -use midenc_session::Session; - -mod codegen; -mod link; -mod parse; -mod rewrite; -mod sema; - -pub use self::codegen::{CodegenStage, Compiled}; -pub use self::link::{LinkerStage, MaybeLinked}; -pub use self::parse::{ParseOutput, ParseStage}; -pub use self::rewrite::ApplyRewritesStage; -pub use self::sema::SemanticAnalysisStage; diff --git a/midenc-compile/src/stages/parse.rs b/midenc-compile/src/stages/parse.rs deleted file mode 100644 index 1d9d491c9..000000000 --- a/midenc-compile/src/stages/parse.rs +++ /dev/null @@ -1,111 +0,0 @@ -use midenc_session::InputFile; -use std::path::Path; -use wasm::WasmTranslationConfig; - -use super::*; - -/// This represents the output of the parser, depending on the type -/// of input that was parsed/loaded. -pub enum ParseOutput { - /// We parsed HIR into the AST from text - Ast(Box), - /// We parsed HIR from a Wasm module or other binary format - Hir(Box), -} - -/// This stage of compilation is where we parse input files into the -/// earliest representation supported by the input file type. Later -/// stages will handle lowering as needed. -pub struct ParseStage; -impl Stage for ParseStage { - type Input = InputFile; - type Output = ParseOutput; - - fn run( - &mut self, - input: Self::Input, - _analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult { - use midenc_session::{FileType, InputType}; - // Track when compilation began - let file_type = input.file_type(); - match &input.file { - InputType::Real(ref path) => match file_type { - FileType::Hir => self.parse_ast_from_file(path.as_ref(), &session), - FileType::Wasm => self.parse_hir_from_wasm_file(path.as_ref(), &session), - unsupported => unreachable!("unsupported file type: {unsupported}"), - }, - InputType::Stdin { name, ref input } => match file_type { - FileType::Hir => self.parse_ast_from_bytes(&input, &session), - FileType::Wasm => self.parse_hir_from_wasm_bytes( - &input, - &session, - &WasmTranslationConfig { - module_name_fallback: name.to_string().clone(), - generate_native_debuginfo: false, - parse_wasm_debuginfo: false, - }, - ), - unsupported => unreachable!("unsupported file type: {unsupported}"), - }, - } - } -} -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 bytes = Vec::with_capacity(1024); - file.read_to_end(&mut bytes)?; - self.parse_ast_from_bytes(&bytes, session) - } - - fn parse_ast_from_bytes(&self, bytes: &[u8], session: &Session) -> CompilerResult { - use miden_hir::parser::Parser; - use std::io::{Error, ErrorKind}; - - let source = core::str::from_utf8(bytes).map_err(|_| { - CompilerError::Io(Error::new( - ErrorKind::InvalidInput, - "input is not valid utf-8", - )) - })?; - let parser = Parser::new(session); - let ast = Box::new(parser.parse_str(source)?); - session.emit(&ast)?; - - Ok(ParseOutput::Ast(ast)) - } - - fn parse_hir_from_wasm_file( - &self, - path: &Path, - session: &Session, - ) -> CompilerResult { - use std::io::Read; - - let mut file = std::fs::File::open(path)?; - let mut bytes = Vec::with_capacity(1024); - file.read_to_end(&mut bytes)?; - let file_name = path.file_stem().unwrap().to_str().unwrap().to_owned(); - let config = wasm::WasmTranslationConfig { - module_name_fallback: file_name, - generate_native_debuginfo: false, - parse_wasm_debuginfo: false, - }; - self.parse_hir_from_wasm_bytes(&bytes, session, &config) - } - - fn parse_hir_from_wasm_bytes( - &self, - bytes: &[u8], - session: &Session, - config: &WasmTranslationConfig, - ) -> CompilerResult { - let module = wasm::translate_module(bytes, config, &session.diagnostics)?; - - Ok(ParseOutput::Hir(Box::new(module))) - } -} diff --git a/midenc-compile/src/stages/rewrite.rs b/midenc-compile/src/stages/rewrite.rs deleted file mode 100644 index cfbd46c77..000000000 --- a/midenc-compile/src/stages/rewrite.rs +++ /dev/null @@ -1,61 +0,0 @@ -use miden_hir::RewritePassRegistration; -use miden_hir_transform as transforms; - -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; - - fn enabled(&self, session: &Session) -> bool { - !session.parse_only() - } - - fn run( - &mut self, - mut input: Self::Input, - analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult { - use miden_hir::pass::{ModuleRewritePassAdapter, RewriteSet}; - - // 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)); - - // If no rewrites were explicitly enabled, and conversion to Miden Assembly is, - // then we must ensure that the basic transformation passes are applied. - // - // Otherwise, assume that the intent was to skip those rewrites and do not add them - let mut rewrites = RewriteSet::default(); - if registered.is_empty() { - if session.should_codegen() { - rewrites.push(ModuleRewritePassAdapter::new( - transforms::SplitCriticalEdges, - )); - rewrites.push(ModuleRewritePassAdapter::new(transforms::Treeify)); - rewrites.push(ModuleRewritePassAdapter::new(transforms::InlineBlocks)); - } - } else { - rewrites.extend(registered.into_iter().map(|(_, r)| r)); - } - - rewrites.apply(&mut input, analyses, session)?; - - Ok(input) - } -} diff --git a/midenc-compile/src/stages/sema.rs b/midenc-compile/src/stages/sema.rs deleted file mode 100644 index 1823d8cd4..000000000 --- a/midenc-compile/src/stages/sema.rs +++ /dev/null @@ -1,32 +0,0 @@ -use super::*; - -/// This stage of compilation takes the output of the parsing -/// stage and loads it into an HIR module for later stages. -/// -/// This may involve additional validation/semantic analysis, hence the name. -pub struct SemanticAnalysisStage; -impl Stage for SemanticAnalysisStage { - type Input = ParseOutput; - type Output = Box; - - fn enabled(&self, session: &Session) -> bool { - !session.parse_only() - } - - fn run( - &mut self, - input: Self::Input, - analyses: &mut AnalysisManager, - session: &Session, - ) -> CompilerResult { - match input { - 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) - } - ParseOutput::Hir(module) => Ok(module), - } - } -} diff --git a/midenc-driver/Cargo.toml b/midenc-driver/Cargo.toml deleted file mode 100644 index a0f118fc4..000000000 --- a/midenc-driver/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "midenc-driver" -description = "The driver for midenc, the Miden compiler" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -homepage.workspace = true -documentation.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -anyhow.workspace = true -clap.workspace = true -miden-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 deleted file mode 100644 index 58f06f4da..000000000 --- a/midenc-driver/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -mod midenc; - -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 ocurred 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, -} - -/// Run the driver as if it was invoked from the command-line -pub fn run(cwd: P, args: A) -> Result<(), DriverError> -where - P: Into, - A: IntoIterator, -{ - match Midenc::run(cwd, args) { - Err(DriverError::Compile(midenc_compile::CompilerError::Stopped)) => Ok(()), - result => result, - } -} diff --git a/midenc-driver/src/midenc.rs b/midenc-driver/src/midenc.rs deleted file mode 100644 index 49330b616..000000000 --- a/midenc-driver/src/midenc.rs +++ /dev/null @@ -1,210 +0,0 @@ -use std::ffi::OsString; -use std::path::PathBuf; -use std::sync::Arc; - -use clap::{ColorChoice, Parser, Subcommand}; -use miden_diagnostics::Emitter; -use miden_hir::FunctionIdent; -use midenc_compile as compile; -use midenc_session::{InputFile, TargetEnv, VerbosityFlag, Warnings}; - -use super::DriverError; - -/// This struct provides the command-line interface used by `midenc` -#[derive(Debug, Parser)] -#[command(name = "midenc")] -#[command(author, version, about = "A compiler for Miden Assembly", long_about = None)] -pub struct Midenc { - #[command(subcommand)] - command: Commands, -} - -#[derive(Debug, Subcommand)] -enum Commands { - Compile(compile::Compiler), - /// Execute a compiled function using the Miden VM emulator. - /// - /// The emulator is more restrictive, but is faster than the Miden VM, and - /// provides a wider array of debugging and introspection features when troubleshooting - /// programs compiled by `midenc`. - Exec { - /// Specify one or more input files to compile as part of the program to execute - /// - /// You may use `-` as a file name to read a file from stdin. - #[arg(required(true), value_name = "FILE")] - input: InputFile, - /// Arguments to place on the operand stack before calling the program entrypoint. - /// - /// Arguments will be pushed on the operand stack in the order of appearance, - /// - /// Example: `-- a b` will push `a` on the stack, then `b`. - /// - /// These arguments must be valid field element values expressed in decimal format. - #[arg(last(true), value_name = "ARGV")] - args: Vec, - /// Specify what type and level of informational output to emit - #[arg( - long = "verbose", - short = 'v', - value_name = "LEVEL", - value_enum, - default_value_t = VerbosityFlag::Info, - default_missing_value = "debug", - help_heading = "Diagnostics", - )] - verbosity: VerbosityFlag, - /// Specify how warnings should be treated by the compiler. - #[arg( - long, - short = 'W', - value_name = "LEVEL", - value_enum, - default_value_t = Warnings::All, - help_heading = "Diagnostics", - )] - 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, - /// Write all intermediate compiler artifacts to `` - /// - /// Defaults to a directory named `target` in the current working directory - #[arg( - long, - value_name = "DIR", - hide(true), - env = "MIDENC_TARGET_DIR", - help_heading = "Output" - )] - target_dir: Option, - /// Specify the fully-qualified name of the function to invoke as the program entrypoint - /// - /// For example, `foo::bar` - #[arg(long, short = 'e', value_name = "NAME")] - entrypoint: Option, - }, - /// Compile and run a program with the Miden VM - /// - /// The program will be compiled to Miden Assembly and then run with the Miden VM. - /// - /// The inputs given must constitute a valid executable program. - Run { - /// Specify one or more input files to compile as part of the program to execute - /// - /// You may use `-` as a file name to read a file from stdin. - #[arg(required(true), value_name = "FILE")] - input: InputFile, - /// Arguments to place on the operand stack before calling the program entrypoint. - /// - /// Arguments will be pushed on the operand stack in the order of appearance, - /// - /// Example: `-- a b` will push `a` on the stack, then `b`. - /// - /// These arguments must be valid field element values expressed in decimal format. - #[arg(last(true), value_name = "ARGV")] - args: Vec, - /// Specify what type and level of informational output to emit - #[arg( - long = "verbose", - short = 'v', - value_name = "LEVEL", - value_enum, - default_value_t = VerbosityFlag::Info, - default_missing_value = "debug", - help_heading = "Diagnostics", - )] - verbosity: VerbosityFlag, - /// Specify how warnings should be treated by the compiler. - #[arg( - long, - short = 'W', - value_name = "LEVEL", - value_enum, - default_value_t = Warnings::All, - help_heading = "Diagnostics", - )] - 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, - /// Write all intermediate compiler artifacts to `` - /// - /// Defaults to a directory named `target` in the current working directory - #[arg( - long, - value_name = "DIR", - hide(true), - env = "MIDENC_TARGET_DIR", - help_heading = "Output" - )] - target_dir: Option, - /// The target environment to compile for - #[arg(long, value_name = "TARGET", hide(true), default_value_t = TargetEnv::Base)] - target: TargetEnv, - /// Specify the fully-qualified name of the function to invoke as the program entrypoint - /// - /// For example, `foo::bar` - #[arg(long, short = 'e', value_name = "NAME")] - entrypoint: Option, - }, -} - -impl Midenc { - pub fn run(cwd: P, args: A) -> Result<(), DriverError> - where - P: Into, - A: IntoIterator, - { - Self::run_with_emitter(cwd, args, None) - } - - pub fn run_with_emitter( - cwd: P, - args: A, - emitter: Option>, - ) -> Result<(), DriverError> - where - P: Into, - A: IntoIterator, - { - let command = ::command(); - let command = command.mut_subcommand("compile", compile::register_flags); - - let mut matches = command.try_get_matches_from(args)?; - let compile_matches = matches - .subcommand_matches("compile") - .cloned() - .unwrap_or_default(); - let cli = ::from_arg_matches_mut(&mut matches) - .map_err(format_error::)?; - - cli.invoke(cwd.into(), emitter, compile_matches) - } - - fn invoke( - self, - cwd: PathBuf, - emitter: Option>, - matches: clap::ArgMatches, - ) -> Result<(), DriverError> { - 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)), - } - } - _ => unimplemented!(), - } - } -} - -fn format_error(err: clap::Error) -> clap::Error { - let mut cmd = I::command(); - err.format(&mut cmd) -} diff --git a/midenc-session/Cargo.toml b/midenc-session/Cargo.toml deleted file mode 100644 index 48f3b25ea..000000000 --- a/midenc-session/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "midenc-session" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -homepage.workspace = true -documentation.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -atty = "0.2" -clap.workspace = true -inventory.workspace = true -miden-diagnostics.workspace = true -miden-hir-symbol.workspace = true -rustc-hash.workspace = true -thiserror.workspace = true diff --git a/midenc-session/src/duration.rs b/midenc-session/src/duration.rs deleted file mode 100644 index 52cf2d272..000000000 --- a/midenc-session/src/duration.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::fmt; -use std::time::{Duration, Instant}; - -pub struct HumanDuration(Duration); -impl HumanDuration { - pub fn since(i: Instant) -> Self { - Self(Instant::now().duration_since(i)) - } - - /// Get this duration as an f64 representing the duration in fractional seconds - #[inline] - pub fn as_secs_f64(&self) -> f64 { - self.0.as_secs_f64() - } -} -impl From for HumanDuration { - fn from(d: Duration) -> Self { - Self(d) - } -} -impl fmt::Display for HumanDuration { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let t = self.0.as_secs(); - let alt = f.alternate(); - macro_rules! try_unit { - ($secs:expr, $sg:expr, $pl:expr, $s:expr) => { - let cnt = t / $secs; - if cnt == 1 { - if alt { - return write!(f, "{}{}", cnt, $s); - } else { - return write!(f, "{} {}", cnt, $sg); - } - } else if cnt > 1 { - if alt { - return write!(f, "{}{}", cnt, $s); - } else { - return write!(f, "{} {}", cnt, $pl); - } - } - }; - } - - if t > 0 { - try_unit!(365 * 24 * 60 * 60, "year", "years", "y"); - try_unit!(7 * 24 * 60 * 60, "week", "weeks", "w"); - try_unit!(24 * 60 * 60, "day", "days", "d"); - try_unit!(60 * 60, "hour", "hours", "h"); - try_unit!(60, "minute", "minutes", "m"); - try_unit!(1, "second", "seconds", "s"); - } else { - // Time was too precise for the standard path, use millis - let t = self.0.as_millis(); - if t > 0 { - return write!(f, "{}{}", t, if alt { "ms" } else { " milliseconds" }); - } - } - write!(f, "0{}", if alt { "s" } else { " seconds" }) - } -} diff --git a/midenc-session/src/emit.rs b/midenc-session/src/emit.rs deleted file mode 100644 index 19e58b5d9..000000000 --- a/midenc-session/src/emit.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::fs::File; -use std::io::Write; -use std::path::Path; - -use miden_hir_symbol::Symbol; - -use crate::OutputType; - -pub trait Emit { - /// The name of this item, if applicable - fn name(&self) -> Option; - /// The output type associated with this item - fn output_type(&self) -> OutputType; - /// Write this item to standard output - fn write_to_stdout(&self) -> std::io::Result<()> { - let stdout = std::io::stdout().lock(); - self.write_to(stdout) - } - /// Write this item to the given file path - fn write_to_file(&self, path: &Path) -> std::io::Result<()> { - if let Some(dir) = path.parent() { - std::fs::create_dir_all(dir)?; - } - let file = File::create(path)?; - self.write_to(file) - } - /// Write this item to the given [std::io::Write] handle - fn write_to(&self, writer: W) -> std::io::Result<()>; -} - -impl Emit for Box { - #[inline] - fn name(&self) -> Option { - (**self).name() - } - #[inline] - fn output_type(&self) -> OutputType { - (**self).output_type() - } - #[inline] - fn write_to_stdout(&self) -> std::io::Result<()> { - (**self).write_to_stdout() - } - #[inline] - fn write_to_file(&self, path: &Path) -> std::io::Result<()> { - (**self).write_to_file(path) - } - #[inline] - fn write_to(&self, writer: W) -> std::io::Result<()> { - (**self).write_to(writer) - } -} diff --git a/midenc-session/src/flags.rs b/midenc-session/src/flags.rs deleted file mode 100644 index 8d36c4990..000000000 --- a/midenc-session/src/flags.rs +++ /dev/null @@ -1,88 +0,0 @@ -pub struct CompileFlag { - pub name: &'static str, - pub short: Option, - pub long: Option<&'static str>, - pub help: Option<&'static str>, - pub help_heading: Option<&'static str>, - pub env: Option<&'static str>, - pub action: FlagAction, - pub default_missing_value: Option<&'static str>, - pub default_value: Option<&'static str>, -} -impl CompileFlag { - pub const fn new(name: &'static str) -> Self { - Self { - name, - short: None, - long: None, - help: None, - help_heading: None, - env: None, - action: FlagAction::Set, - default_missing_value: None, - default_value: None, - } - } - - pub const fn short(mut self, short: char) -> Self { - self.short = Some(short); - self - } - - pub const fn long(mut self, long: &'static str) -> Self { - self.long = Some(long); - self - } - - pub const fn action(mut self, action: FlagAction) -> Self { - self.action = action; - self - } - - pub const fn help(mut self, help: &'static str) -> Self { - self.help = Some(help); - self - } - - pub const fn help_heading(mut self, help_heading: &'static str) -> Self { - self.help_heading = Some(help_heading); - self - } - - pub const fn env(mut self, env: &'static str) -> Self { - self.env = Some(env); - self - } - - pub const fn default_value(mut self, value: &'static str) -> Self { - self.default_value = Some(value); - self - } - - pub const fn default_missing_value(mut self, value: &'static str) -> Self { - self.default_missing_value = Some(value); - self - } -} - -#[derive(Debug, Copy, Clone)] -pub enum FlagAction { - Set, - Append, - SetTrue, - SetFalse, - Count, -} -impl From for clap::ArgAction { - fn from(action: FlagAction) -> Self { - match action { - FlagAction::Set => Self::Set, - FlagAction::Append => Self::Append, - FlagAction::SetTrue => Self::SetTrue, - FlagAction::SetFalse => Self::SetFalse, - FlagAction::Count => Self::Count, - } - } -} - -inventory::collect!(CompileFlag); diff --git a/midenc-session/src/inputs.rs b/midenc-session/src/inputs.rs deleted file mode 100644 index 2c4584dd8..000000000 --- a/midenc-session/src/inputs.rs +++ /dev/null @@ -1,211 +0,0 @@ -use std::ffi::OsStr; -use std::fmt; -use std::path::{Path, PathBuf}; - -use miden_diagnostics::FileName; - -/// An error that occurs when detecting the file type of an input -#[derive(Debug, thiserror::Error)] -pub enum InvalidInputError { - /// Occurs if an unsupported file type is given as an input - #[error("invalid input file '{}': unsupported file type", .0.display())] - UnsupportedFileType(std::path::PathBuf), - /// We attemped to detecth the file type from the raw bytes, but failed - #[error("could not detect file type of input")] - UnrecognizedFileType, - /// Unable to read input file - #[error(transparent)] - Io(#[from] std::io::Error), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum InputType { - Real(PathBuf), - Stdin { name: FileName, input: Vec }, -} - -/// This enum represents the types of raw inputs provided to the compiler -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct InputFile { - pub file: InputType, - file_type: FileType, -} -impl InputFile { - /// Get an [InputFile] representing the contents of `path`. - /// - /// This function returns an error if the contents are not a valid supported file type. - pub fn from_path>(path: P) -> Result { - let path = path.as_ref(); - let file_type = FileType::try_from(path)?; - match file_type { - FileType::Hir | FileType::Wasm | FileType::Masm | FileType::Masl => Ok(Self { - file: InputType::Real(path.to_path_buf()), - file_type, - }), - // We do not yet have frontends for these file types - FileType::Wat => Err(InvalidInputError::UnsupportedFileType(path.to_path_buf())), - } - } - - /// Get an [InputFile] representing the contents received from standard input. - /// - /// This function returns an error if the contents are not a valid supported file type. - pub fn from_stdin(name: FileName) -> Result { - use std::io::Read; - - let mut input = Vec::with_capacity(1024); - std::io::stdin().read_to_end(&mut input)?; - let file_type = FileType::detect(&input)?; - match file_type { - FileType::Hir | FileType::Wasm => Ok(Self { - file: InputType::Stdin { name, input }, - file_type, - }), - // We do not yet have frontends for these file types - FileType::Masm | FileType::Masl | FileType::Wat => Err( - InvalidInputError::UnsupportedFileType(PathBuf::from("stdin")), - ), - } - } - - pub fn file_type(&self) -> FileType { - self.file_type - } - - pub fn as_path(&self) -> Option<&Path> { - match &self.file { - InputType::Real(ref path) => Some(path), - _ => None, - } - } - - pub fn is_real(&self) -> bool { - matches!(self.file, InputType::Real(_)) - } - - pub fn filestem(&self) -> &str { - match &self.file { - InputType::Real(ref path) => path.file_stem().unwrap().to_str().unwrap(), - InputType::Stdin { .. } => "noname", - } - } -} -impl clap::builder::ValueParserFactory for InputFile { - type Parser = InputFileParser; - - fn value_parser() -> Self::Parser { - InputFileParser - } -} - -#[doc(hidden)] -#[derive(Clone)] -pub struct InputFileParser; -impl clap::builder::TypedValueParser for InputFileParser { - type Value = InputFile; - - fn parse_ref( - &self, - _cmd: &clap::Command, - _arg: Option<&clap::Arg>, - value: &OsStr, - ) -> Result { - use clap::error::{Error, ErrorKind}; - - let input_file = match value.to_str() { - Some("-") => InputFile::from_stdin(FileName::Virtual("stdin".into())).map_err( - |err| match err { - InvalidInputError::Io(err) => Error::raw(ErrorKind::Io, err), - err => Error::raw(ErrorKind::ValueValidation, err), - }, - )?, - Some(_) | None => { - InputFile::from_path(PathBuf::from(value)).map_err(|err| match err { - InvalidInputError::Io(err) => Error::raw(ErrorKind::Io, err), - err => Error::raw(ErrorKind::ValueValidation, err), - })? - } - }; - - match &input_file.file { - InputType::Real(path) => { - if path.exists() { - if path.is_file() { - Ok(input_file) - } else { - Err(Error::raw( - ErrorKind::ValueValidation, - format!("invalid input '{}': not a file", path.display()), - )) - } - } else { - Err(Error::raw( - ErrorKind::ValueValidation, - format!("invalid input '{}': file does not exist", path.display()), - )) - } - } - InputType::Stdin { .. } => Ok(input_file), - } - } -} - -/// This represents the file types recognized by the compiler -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum FileType { - Hir, - Masm, - Masl, - Wasm, - Wat, -} -impl fmt::Display for FileType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Hir => f.write_str("hir"), - Self::Masm => f.write_str("masm"), - Self::Masl => f.write_str("masl"), - Self::Wasm => f.write_str("wasm"), - Self::Wat => f.write_str("wat"), - } - } -} -impl FileType { - pub fn detect(bytes: &[u8]) -> Result { - if bytes.starts_with(b"\0asm") { - return Ok(FileType::Wasm); - } - - fn is_masm_top_level_item(line: &str) -> bool { - line.starts_with("const.") || line.starts_with("export.") || line.starts_with("proc.") - } - - if let Ok(content) = core::str::from_utf8(bytes) { - if content.starts_with("(module ") { - return Ok(FileType::Wat); - } - if content.starts_with("module ") { - return Ok(FileType::Hir); - } - if content.lines().any(is_masm_top_level_item) { - return Ok(FileType::Masm); - } - } - - Err(InvalidInputError::UnrecognizedFileType) - } -} -impl TryFrom<&Path> for FileType { - type Error = InvalidInputError; - - fn try_from(path: &Path) -> Result { - match path.extension().and_then(|ext| ext.to_str()) { - Some("hir") => Ok(FileType::Hir), - Some("masm") => Ok(FileType::Masm), - Some("masl") => Ok(FileType::Masl), - Some("wasm") => Ok(FileType::Wasm), - Some("wat") => Ok(FileType::Wat), - _ => Err(InvalidInputError::UnsupportedFileType(path.to_path_buf())), - } - } -} diff --git a/midenc-session/src/lib.rs b/midenc-session/src/lib.rs deleted file mode 100644 index 21078f707..000000000 --- a/midenc-session/src/lib.rs +++ /dev/null @@ -1,329 +0,0 @@ -mod duration; -mod emit; -mod flags; -mod inputs; -mod options; -mod outputs; -mod statistics; - -pub use self::duration::HumanDuration; -pub use self::emit::Emit; -pub use self::flags::{CompileFlag, FlagAction}; -pub use self::inputs::{FileType, InputFile, InputType, InvalidInputError}; -pub use self::options::*; -pub use self::outputs::{OutputFile, OutputFiles, OutputType, OutputTypeSpec, OutputTypes}; -pub use self::statistics::Statistics; - -use std::fmt; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -use clap::ValueEnum; -use miden_diagnostics::{CodeMap, DiagnosticsHandler, Emitter}; -use miden_hir_symbol::Symbol; - -/// The type of project being compiled -#[derive(Debug, Copy, Clone, Default)] -pub enum ProjectType { - /// Compile a Miden program that can be run on the Miden VM - #[default] - Program, - /// Compile a Miden library which can be linked into a program - Library, -} -impl ProjectType { - pub fn default_for_target(target: TargetEnv) -> Self { - match target { - // We default to compiling a program unless we find later - // that we do not have an entrypoint. - TargetEnv::Base | TargetEnv::Rollup => Self::Program, - // The emulator can run either programs or individual library functions, - // so we compile as a library and delegate the choice of how to run it - // to the emulator - TargetEnv::Emu => Self::Library, - } - } -} - -/// 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 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 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 Session { - pub fn new( - target: TargetEnv, - input: InputFile, - output_dir: Option, - output_file: Option, - tmp_dir: Option, - options: Options, - emitter: Option>, - ) -> 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(), - emitter.unwrap_or_else(|| options.default_emitter()), - )); - - let output_files = match output_file { - None => { - let output_dir = output_dir.unwrap_or_default(); - let stem = options - .name - .clone() - .unwrap_or_else(|| input.filestem().to_owned()); - - OutputFiles::new( - stem, - output_dir, - None, - tmp_dir, - options.output_types.clone(), - ) - } - Some(out_file) => OutputFiles::new( - out_file - .filestem() - .unwrap_or_default() - .to_str() - .unwrap() - .to_string(), - output_dir.unwrap_or_default(), - Some(out_file), - tmp_dir, - options.output_types.clone(), - ), - }; - - let project_type = ProjectType::default_for_target(target); - Self { - project_type, - target, - options, - codemap, - diagnostics, - sysroot, - 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 - } - - #[doc(hidden)] - pub fn with_arg_matches(mut self, matches: clap::ArgMatches) -> Self { - self.arg_matches = matches; - self - } - - /// 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 - } - - /// The name of this session (used as the name of the project, output file, etc.) - pub fn name(&self) -> String { - self.options - .name - .clone() - .or_else(|| { - if self.input.is_real() { - Some(self.input.filestem().to_string()) - } else { - None - } - }) - .unwrap_or_else(|| { - self.options - .current_dir - .file_name() - .unwrap() - .to_string_lossy() - .into_owned() - }) - } - - pub fn out_filename(&self, outputs: &OutputFiles, progname: Symbol) -> OutputFile { - let default_filename = self.filename_for_input(outputs, progname); - let out_filename = outputs - .outputs - .get(&OutputType::Masl) - .and_then(|s| s.to_owned()) - .or_else(|| outputs.out_file.clone()) - .unwrap_or(default_filename); - - if let OutputFile::Real(ref path) = out_filename { - self.check_file_is_writeable(path); - } - - out_filename - } - - pub fn filename_for_input(&self, outputs: &OutputFiles, progname: Symbol) -> OutputFile { - match self.project_type { - ProjectType::Program => { - let out_filename = outputs.path(OutputType::Masl); - if let OutputFile::Real(ref path) = out_filename { - OutputFile::Real(path.with_extension(OutputType::Masl.extension())) - } else { - out_filename - } - } - ProjectType::Library => OutputFile::Real( - outputs - .out_dir - .join(format!("{progname}.{}", OutputType::Masl.extension())), - ), - } - } - - fn check_file_is_writeable(&self, file: &Path) { - if let Ok(m) = file.metadata() { - if m.permissions().readonly() { - self.diagnostics - .fatal(format!("file is not writeable: {}", file.display())) - .raise(); - } - } - } - - pub fn parse_only(&self) -> bool { - self.options.output_types.parse_only() - } - - pub fn should_codegen(&self) -> bool { - self.options.output_types.should_codegen() - } - - pub fn should_link(&self) -> bool { - self.options.output_types.should_link() - } - - pub fn should_emit(&self, ty: OutputType) -> bool { - self.options.output_types.contains_key(&ty) - } - - /// 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)), - OutputFile::Stdout => None, - } - } else { - None - } - } - - /// Emit an item to stdout/file system depending on the current configuration - 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) { - 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)?; - } - OutputFile::Stdout => { - item.write_to_stdout()?; - } - } - } - - Ok(()) - } -} - -/// This enum describes the different target environments targetable by the compiler -#[derive(Debug, Copy, Clone, Default, ValueEnum)] -pub enum TargetEnv { - /// The emulator environment, which has a more restrictive instruction set - Emu, - /// The default Miden VM environment - #[default] - Base, - /// The Miden Rollup environment, using the Rollup kernel - Rollup, -} -impl fmt::Display for TargetEnv { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Emu => f.write_str("emu"), - Self::Base => f.write_str("base"), - Self::Rollup => f.write_str("rollup"), - } - } -} diff --git a/midenc-session/src/options/mod.rs b/midenc-session/src/options/mod.rs deleted file mode 100644 index f5dbd3536..000000000 --- a/midenc-session/src/options/mod.rs +++ /dev/null @@ -1,189 +0,0 @@ -use std::fmt; -use std::path::PathBuf; -use std::str::FromStr; -use std::sync::Arc; - -use clap::ValueEnum; -use miden_diagnostics::term::termcolor::ColorChoice; -use miden_diagnostics::{DiagnosticsConfig, Emitter, Verbosity}; - -use crate::OutputTypes; - -/// 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 optimization level for the current program - pub optimize: OptLevel, - /// 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 location of the libraries which are shipped with the compiler - pub sysroot: Option, - /// Whether, and how, to color terminal output - pub color: ColorChoice, - /// The current diagnostics configuration - pub diagnostics: DiagnosticsConfig, - /// The current working directory of the compiler - pub current_dir: PathBuf, - /// Print IR to stdout after each pass - pub print_ir_after_all: bool, - /// Print IR to stdout each time the named pass is applied - pub print_ir_after_pass: Option, -} -impl Default for Options { - fn default() -> Self { - let current_dir = std::env::current_dir().expect("could not get working directory"); - Self::new(current_dir) - } -} -impl Options { - pub fn new(current_dir: PathBuf) -> Self { - Self { - name: None, - optimize: OptLevel::None, - output_types: Default::default(), - search_paths: vec![], - sysroot: None, - color: Default::default(), - diagnostics: Default::default(), - current_dir, - print_ir_after_all: false, - print_ir_after_pass: None, - } - } - - pub fn with_color(mut self, color: ColorChoice) -> Self { - self.color = color; - self - } - - pub fn with_verbosity(mut self, verbosity: Verbosity) -> Self { - self.diagnostics.verbosity = verbosity; - 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 - } - - 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. - pub fn default_emitter(&self) -> Arc { - use miden_diagnostics::{DefaultEmitter, NullEmitter}; - - match self.diagnostics.verbosity { - Verbosity::Silent => Arc::new(NullEmitter::new(self.color)), - _ => Arc::new(DefaultEmitter::new(self.color)), - } - } -} - -/// This enum describes the degree to which compiled programs will be optimized -#[derive(Debug, Copy, Clone, Default, ValueEnum)] -pub enum OptLevel { - /// No optimizations at all - None, - /// Only basic optimizations are applied, e.g. constant propagation - Basic, - /// Most optimizations are applied, except when the cost is particularly high. - #[default] - Balanced, - /// All optimizations are applied, with all tradeoffs in favor of runtime performance - Max, - /// Most optimizations are applied, but tuned to trade runtime performance for code size - Size, - /// Only optimizations which reduce code size are applied - SizeMin, -} - -/// This enum represents the behavior of the compiler with regard to warnings -#[derive(Debug, Copy, Clone, Default, ValueEnum)] -pub enum Warnings { - /// Disable all warnings - None, - /// Enable all warnings - #[default] - All, - /// Promotes warnings to errors - Error, -} -impl fmt::Display for Warnings { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::None => f.write_str("none"), - Self::All => f.write_str("auto"), - Self::Error => f.write_str("error"), - } - } -} -impl FromStr for Warnings { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "none" => Ok(Self::None), - "all" => Ok(Self::All), - "error" => Ok(Self::Error), - _ => Err(()), - } - } -} - -/// This enum represents the type of messages produced by the compiler during execution -#[derive(Debug, Copy, Clone, Default, ValueEnum)] -pub enum VerbosityFlag { - /// Emit additional debug/trace information during compilation - Debug, - /// Emit the standard informational, warning, and error messages - #[default] - Info, - /// Only emit warnings and errors - Warning, - /// Only emit errors - Error, - /// 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 deleted file mode 100644 index 504e44227..000000000 --- a/midenc-session/src/outputs.rs +++ /dev/null @@ -1,313 +0,0 @@ -use std::collections::BTreeMap; -use std::ffi::OsStr; -use std::fmt; -use std::path::{Path, PathBuf}; -use std::str::FromStr; - -use clap::ValueEnum; - -/// This enum represents the type of outputs the compiler can produce -#[derive(Debug, Copy, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] -pub enum OutputType { - /// The compiler will emit the abstract syntax tree of the input, if applicable - Ast, - /// The compiler will emit Miden IR - Hir, - /// The compiler will emit Miden Assembly - Masm, - /// The compiler will emit a Miden Assembly program or library - #[default] - Masl, -} -impl OutputType { - pub fn extension(&self) -> &'static str { - match self { - Self::Ast => "ast", - Self::Hir => "hir", - Self::Masm => "masm", - Self::Masl => "masl", - } - } - - pub fn shorthand_display() -> String { - format!( - "`{}`, `{}`, `{}`, `{}`", - Self::Ast, - Self::Hir, - Self::Masm, - Self::Masl, - ) - } -} -impl fmt::Display for OutputType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Ast => f.write_str("ast"), - Self::Hir => f.write_str("hir"), - Self::Masm => f.write_str("masm"), - Self::Masl => f.write_str("masl"), - } - } -} -impl FromStr for OutputType { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "ast" => Ok(Self::Ast), - "hir" => Ok(Self::Hir), - "masm" => Ok(Self::Masm), - "masl" => Ok(Self::Masl), - _ => Err(()), - } - } -} - -#[derive(Debug, Clone)] -pub enum OutputFile { - Real(PathBuf), - Stdout, -} -impl OutputFile { - pub fn parent(&self) -> Option<&Path> { - match self { - Self::Real(ref path) => path.parent(), - Self::Stdout => None, - } - } - - pub fn filestem(&self) -> Option<&OsStr> { - match self { - Self::Real(ref path) => path.file_stem(), - Self::Stdout => Some(OsStr::new("stdout")), - } - } - - pub fn is_stdout(&self) -> bool { - matches!(self, Self::Stdout) - } - - pub fn is_tty(&self) -> bool { - match self { - Self::Real(_) => false, - Self::Stdout => atty::is(atty::Stream::Stdout), - } - } - - pub fn as_path(&self) -> &Path { - match self { - Self::Real(ref path) => path.as_ref(), - Self::Stdout => Path::new("stdout"), - } - } - - pub fn file_for_writing( - &self, - outputs: &OutputFiles, - ty: OutputType, - name: Option<&str>, - ) -> PathBuf { - match self { - Self::Real(ref path) => path.clone(), - Self::Stdout => outputs.temp_path(ty, name), - } - } -} - -#[derive(Debug)] -pub struct OutputFiles { - stem: String, - pub out_dir: PathBuf, - pub out_file: Option, - pub tmp_dir: Option, - pub outputs: OutputTypes, -} -impl OutputFiles { - pub fn new( - stem: String, - out_dir: PathBuf, - out_file: Option, - tmp_dir: Option, - outputs: OutputTypes, - ) -> Self { - Self { - stem, - out_dir, - out_file, - tmp_dir, - outputs, - } - } - - pub fn path(&self, ty: OutputType) -> OutputFile { - 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))) - } - - pub fn output_path(&self, ty: OutputType) -> PathBuf { - let extension = ty.extension(); - if let Some(output_file) = self.outputs.get(&ty) { - match output_file { - Some(OutputFile::Real(ref path)) if path.is_absolute() => { - path.with_extension(extension) - } - Some(OutputFile::Real(ref path)) => { - self.out_dir.join(path).with_extension(extension) - } - Some(OutputFile::Stdout) | None => { - self.with_directory_and_extension(&self.out_dir, extension) - } - } - } else { - self.with_directory_and_extension(&self.out_dir, extension) - } - } - - pub fn temp_path(&self, ty: OutputType, name: Option<&str>) -> PathBuf { - let extension = ty.extension(); - self.temp_path_ext(extension, name) - } - - fn temp_path_ext(&self, ext: &str, name: Option<&str>) -> PathBuf { - let mut extension = String::new(); - - if let Some(name) = name { - extension.push_str(name); - } - - if !ext.is_empty() { - if !extension.is_empty() { - extension.push('.'); - } - extension.push_str(ext); - } - - let tmp_dir = self.tmp_dir.as_ref().unwrap_or(&self.out_dir); - self.with_directory_and_extension(tmp_dir, &extension) - } - - pub fn with_extension(&self, extension: &str) -> PathBuf { - self.with_directory_and_extension(&self.out_dir, extension) - } - - fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf { - let mut path = directory.join(&self.stem); - path.set_extension(extension); - path - } -} - -#[derive(Debug, Clone, Default)] -pub struct OutputTypes(BTreeMap>); -impl OutputTypes { - pub fn new>(entries: I) -> Self { - Self(BTreeMap::from_iter( - entries - .into_iter() - .map(|spec| (spec.output_type, spec.path)), - )) - } - - pub fn get(&self, key: &OutputType) -> Option<&Option> { - self.0.get(key) - } - - pub fn insert(&mut self, key: OutputType, value: Option) { - self.0.insert(key, value); - } - - pub fn contains_key(&self, key: &OutputType) -> bool { - self.0.contains_key(key) - } - - pub fn iter(&self) -> std::collections::btree_map::Iter<'_, OutputType, Option> { - self.0.iter() - } - - pub fn keys(&self) -> std::collections::btree_map::Keys<'_, OutputType, Option> { - self.0.keys() - } - - pub fn values( - &self, - ) -> std::collections::btree_map::Values<'_, OutputType, Option> { - self.0.values() - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn parse_only(&self) -> bool { - !self.0.keys().any(|k| !matches!(k, OutputType::Ast)) - } - - pub fn should_codegen(&self) -> bool { - self.0 - .keys() - .any(|k| matches!(k, OutputType::Masm | OutputType::Masl)) - } - - pub fn should_link(&self) -> bool { - self.0 - .keys() - .any(|k| matches!(k, OutputType::Masm | OutputType::Masl)) - } -} - -/// This type describes an output type with optional path specification -#[derive(Debug, Clone)] -pub struct OutputTypeSpec { - pub output_type: OutputType, - pub path: Option, -} -impl clap::builder::ValueParserFactory for OutputTypeSpec { - type Parser = OutputTypeParser; - - fn value_parser() -> Self::Parser { - OutputTypeParser - } -} - -#[doc(hidden)] -#[derive(Clone)] -pub struct OutputTypeParser; -impl clap::builder::TypedValueParser for OutputTypeParser { - type Value = OutputTypeSpec; - - fn parse_ref( - &self, - _cmd: &clap::Command, - _arg: Option<&clap::Arg>, - value: &OsStr, - ) -> Result { - use clap::error::{Error, ErrorKind}; - - let output_type = value - .to_str() - .ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?; - - let (shorthand, path) = match output_type.split_once('=') { - None => (output_type, None), - Some((shorthand, "-")) => (shorthand, Some(OutputFile::Stdout)), - Some((shorthand, path)) => (shorthand, Some(OutputFile::Real(PathBuf::from(path)))), - }; - let output_type = shorthand.parse::().map_err(|_| { - Error::raw( - ErrorKind::InvalidValue, - format!( - "invalid output type: `{shorthand}` - expected one of: {display}", - display = OutputType::shorthand_display() - ), - ) - })?; - Ok(OutputTypeSpec { output_type, path }) - } -} diff --git a/midenc-session/src/statistics.rs b/midenc-session/src/statistics.rs deleted file mode 100644 index 1a6c39624..000000000 --- a/midenc-session/src/statistics.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::sync::atomic::{AtomicU64, Ordering}; -use std::time::{Duration, Instant}; - -use crate::HumanDuration; - -const NOT_STARTED: u64 = u64::MAX; - -/// This struct contains various statistics about a compilation session -pub struct Statistics { - /// The time at which the compiler session started - start_time: Instant, - /// The elapsed time at which parsing started - /// - /// Parsing here refers to one of two things: - /// - /// 1. Loading of a Wasm module into memory and converting it to HIR - /// 2. Parsing of an HIR module into memory - parse_time: AtomicU64, - /// The elapsed time at which optimizations/rewrites started - opt_time: AtomicU64, - /// The elapsed time at which codegen started - codegen_time: AtomicU64, -} -impl Default for Statistics { - fn default() -> Statistics { - Self::new(Instant::now()) - } -} -impl Statistics { - pub fn new(start_time: Instant) -> Self { - Self { - start_time, - parse_time: AtomicU64::new(NOT_STARTED), - opt_time: AtomicU64::new(NOT_STARTED), - codegen_time: AtomicU64::new(NOT_STARTED), - } - } - - /// Get the duration since the compiler session started - pub fn elapsed(&self) -> HumanDuration { - HumanDuration::since(self.start_time) - } - - /// Get the time spent parsing/loading inputs, if applicable - pub fn parse_time(&self) -> Option { - load_duration(&self.parse_time) - } - - /// Get the time spent optimizing the IR, if applicable - pub fn opt_time(&self) -> Option { - load_duration(&self.opt_time) - } - - /// Get the time spent generating Miden Assembly, if applicable - pub fn codegen_time(&self) -> Option { - load_duration(&self.codegen_time) - } - - /// Record that parsing/loading inputs has completed - pub fn parsing_completed(&self) { - store_duration(&self.parse_time, self.elapsed()) - } - - /// Record that optimization of the IR has completed - pub fn optimization_completed(&self) { - store_duration(&self.opt_time, self.elapsed()) - } - - /// Record that codegen of Miden Assembly has completed - pub fn codegen_completed(&self) { - store_duration(&self.codegen_time, self.elapsed()) - } -} - -fn store_duration(raw_secs_f64: &AtomicU64, duration: HumanDuration) { - let bits = duration.as_secs_f64().to_bits(); - raw_secs_f64.store(bits, Ordering::Relaxed) -} - -fn load_duration(raw_secs_f64: &AtomicU64) -> Option { - match raw_secs_f64.load(Ordering::Relaxed) { - NOT_STARTED => None, - bits => Some(Duration::from_secs_f64(f64::from_bits(bits)).into()), - } -} diff --git a/midenc/Cargo.toml b/midenc/Cargo.toml deleted file mode 100644 index eead2d9f5..000000000 --- a/midenc/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "midenc" -description = "The compiler frontend/executable for Miden" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -homepage.workspace = true -documentation.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -anyhow.workspace = true -env_logger.workspace = true -human-panic = "1.0" -midenc-driver.workspace = true diff --git a/midenc/src/main.rs b/midenc/src/main.rs deleted file mode 100644 index 671524ad2..000000000 --- a/midenc/src/main.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::env; - -use anyhow::anyhow; - -use midenc_driver::{self as driver, DriverError}; - -pub fn main() -> Result<(), DriverError> { - if cfg!(not(debug_assertions)) { - if env::var_os("MIDENC_TRACE").is_none() { - human_panic::setup_panic!(); - } - } - - // Initialize logger - let mut builder = env_logger::Builder::from_env("MIDENC_TRACE"); - builder.format_indent(Some(2)); - if let Ok(precision) = env::var("MIDENC_TRACE_TIMING") { - match precision.as_str() { - "s" => builder.format_timestamp_secs(), - "ms" => builder.format_timestamp_millis(), - "us" => builder.format_timestamp_micros(), - "ns" => builder.format_timestamp_nanos(), - other => { - return Err(DriverError::Failed(anyhow!( - "invalid MIDENC_TRACE_TIMING precision, expected one of [s, ms, us, ns], got '{}'", - other - ))) - } - }; - } else { - builder.format_timestamp(None); - } - builder.init(); - - // Get current working directory - let cwd = env::current_dir()?; - - match driver::run(cwd, env::args_os()) { - Err(DriverError::Clap(err)) => err.exit(), - result => result, - } -} diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 7be3bf391..000000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,5 +0,0 @@ -[toolchain] -channel = "nightly-2023-12-20" -components = ["rustfmt", "rust-src"] -targets = ["wasm32-unknown-unknown"] -profile = "minimal" diff --git a/tests/integration/Cargo.toml b/tests/integration/Cargo.toml deleted file mode 100644 index 9bde2f86b..000000000 --- a/tests/integration/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "miden-integration-tests" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -repository.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true - -[dependencies] -miden-frontend-wasm.workspace = true -miden-hir.workspace = true -miden-hir-transform.workspace = true -miden-codegen-masm.workspace = true -miden-assembly.workspace = true -miden-core.workspace = true -miden-processor.workspace = true -miden-stdlib.workspace = true -miden-diagnostics.workspace = true -midenc-session.workspace = true -expect-test = "1.4.1" -miden-integration-tests-rust-fib = {path = "../rust-apps/fib"} -wasmprinter = "0.2.63" -sha2 = "0.10" -rustc-demangle = {version = "0.1.19", features = ["std"]} -cargo_metadata = "0.18" - -[dev-dependencies] -proptest.workspace = true -concat-idents = "1.1" diff --git a/tests/integration/expected/add_i16.hir b/tests/integration/expected/add_i16.hir deleted file mode 100644 index 156040083..000000000 --- a/tests/integration/expected/add_i16.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = add.wrapping v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/add_i16.masm b/tests/integration/expected/add_i16.masm deleted file mode 100644 index d2d5d7af2..000000000 --- a/tests/integration/expected/add_i16.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32wrapping_add -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/add_i16.wat b/tests/integration/expected/add_i16.wat deleted file mode 100644 index cf0407873..000000000 --- a/tests/integration/expected/add_i16.wat +++ /dev/null @@ -1,17 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.add - i32.extend16_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/add_i32.hir b/tests/integration/expected/add_i32.hir deleted file mode 100644 index 156040083..000000000 --- a/tests/integration/expected/add_i32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = add.wrapping v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/add_i32.masm b/tests/integration/expected/add_i32.masm deleted file mode 100644 index d2d5d7af2..000000000 --- a/tests/integration/expected/add_i32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32wrapping_add -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/add_i32.wat b/tests/integration/expected/add_i32.wat deleted file mode 100644 index 8b540e563..000000000 --- a/tests/integration/expected/add_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.add - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/add_i64.hir b/tests/integration/expected/add_i64.hir deleted file mode 100644 index c09c6a6c3..000000000 --- a/tests/integration/expected/add_i64.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = add.wrapping v1, v0 : i64; - ret v3; -} diff --git a/tests/integration/expected/add_i64.wat b/tests/integration/expected/add_i64.wat deleted file mode 100644 index e48dc39ad..000000000 --- a/tests/integration/expected/add_i64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 1 - local.get 0 - i64.add - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/add_i8.hir b/tests/integration/expected/add_i8.hir deleted file mode 100644 index 156040083..000000000 --- a/tests/integration/expected/add_i8.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = add.wrapping v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/add_i8.masm b/tests/integration/expected/add_i8.masm deleted file mode 100644 index d2d5d7af2..000000000 --- a/tests/integration/expected/add_i8.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32wrapping_add -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/add_i8.wat b/tests/integration/expected/add_i8.wat deleted file mode 100644 index c7ccbc23a..000000000 --- a/tests/integration/expected/add_i8.wat +++ /dev/null @@ -1,17 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.add - i32.extend8_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/add_u16.hir b/tests/integration/expected/add_u16.hir deleted file mode 100644 index 8182fd2f4..000000000 --- a/tests/integration/expected/add_u16.hir +++ /dev/null @@ -1,15 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = add.wrapping v1, v0 : i32; - v4 = const.i32 65535 : i32; - v5 = band v3, v4 : i32; - ret v5; -} diff --git a/tests/integration/expected/add_u16.masm b/tests/integration/expected/add_u16.masm deleted file mode 100644 index fbad9a99c..000000000 --- a/tests/integration/expected/add_u16.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32wrapping_add - push.65535 - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/add_u16.wat b/tests/integration/expected/add_u16.wat deleted file mode 100644 index 16a0cf33e..000000000 --- a/tests/integration/expected/add_u16.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.add - i32.const 65535 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/add_u32.hir b/tests/integration/expected/add_u32.hir deleted file mode 100644 index 156040083..000000000 --- a/tests/integration/expected/add_u32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = add.wrapping v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/add_u32.masm b/tests/integration/expected/add_u32.masm deleted file mode 100644 index d2d5d7af2..000000000 --- a/tests/integration/expected/add_u32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32wrapping_add -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/add_u32.wat b/tests/integration/expected/add_u32.wat deleted file mode 100644 index 8b540e563..000000000 --- a/tests/integration/expected/add_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.add - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/add_u64.hir b/tests/integration/expected/add_u64.hir deleted file mode 100644 index c09c6a6c3..000000000 --- a/tests/integration/expected/add_u64.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = add.wrapping v1, v0 : i64; - ret v3; -} diff --git a/tests/integration/expected/add_u64.wat b/tests/integration/expected/add_u64.wat deleted file mode 100644 index e48dc39ad..000000000 --- a/tests/integration/expected/add_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 1 - local.get 0 - i64.add - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/add_u8.hir b/tests/integration/expected/add_u8.hir deleted file mode 100644 index 6d98f4ab0..000000000 --- a/tests/integration/expected/add_u8.hir +++ /dev/null @@ -1,15 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = add.wrapping v1, v0 : i32; - v4 = const.i32 255 : i32; - v5 = band v3, v4 : i32; - ret v5; -} diff --git a/tests/integration/expected/add_u8.masm b/tests/integration/expected/add_u8.masm deleted file mode 100644 index 34f22f7d1..000000000 --- a/tests/integration/expected/add_u8.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32wrapping_add - push.255 - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/add_u8.wat b/tests/integration/expected/add_u8.wat deleted file mode 100644 index 73c92e8ca..000000000 --- a/tests/integration/expected/add_u8.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.add - i32.const 255 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_bool.hir b/tests/integration/expected/and_bool.hir deleted file mode 100644 index 566332bcd..000000000 --- a/tests/integration/expected/and_bool.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = band v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/and_bool.masm b/tests/integration/expected/and_bool.masm deleted file mode 100644 index 2c3c6db33..000000000 --- a/tests/integration/expected/and_bool.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/and_bool.wat b/tests/integration/expected/and_bool.wat deleted file mode 100644 index 0dd56cd09..000000000 --- a/tests/integration/expected/and_bool.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_i16.hir b/tests/integration/expected/and_i16.hir deleted file mode 100644 index 109333667..000000000 --- a/tests/integration/expected/and_i16.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = band v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/and_i16.masm b/tests/integration/expected/and_i16.masm deleted file mode 100644 index 8ff0c42ac..000000000 --- a/tests/integration/expected/and_i16.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/and_i16.wat b/tests/integration/expected/and_i16.wat deleted file mode 100644 index 8f3374696..000000000 --- a/tests/integration/expected/and_i16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_i32.hir b/tests/integration/expected/and_i32.hir deleted file mode 100644 index 109333667..000000000 --- a/tests/integration/expected/and_i32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = band v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/and_i32.masm b/tests/integration/expected/and_i32.masm deleted file mode 100644 index 8ff0c42ac..000000000 --- a/tests/integration/expected/and_i32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/and_i32.wat b/tests/integration/expected/and_i32.wat deleted file mode 100644 index 8f3374696..000000000 --- a/tests/integration/expected/and_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_i64.hir b/tests/integration/expected/and_i64.hir deleted file mode 100644 index 56725d869..000000000 --- a/tests/integration/expected/and_i64.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = band v1, v0 : i64; - br block1(v3); - -block1(v2: i64): - ret v2; -} diff --git a/tests/integration/expected/and_i64.masm b/tests/integration/expected/and_i64.masm deleted file mode 100644 index fedf28032..000000000 --- a/tests/integration/expected/and_i64.masm +++ /dev/null @@ -1,8 +0,0 @@ -export.entrypoint - exec.checked_and - -end -begin - exec.entrypoint -end - diff --git a/tests/integration/expected/and_i64.wat b/tests/integration/expected/and_i64.wat deleted file mode 100644 index ba86570b6..000000000 --- a/tests/integration/expected/and_i64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 1 - local.get 0 - i64.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_i8.hir b/tests/integration/expected/and_i8.hir deleted file mode 100644 index 109333667..000000000 --- a/tests/integration/expected/and_i8.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = band v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/and_i8.masm b/tests/integration/expected/and_i8.masm deleted file mode 100644 index 8ff0c42ac..000000000 --- a/tests/integration/expected/and_i8.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/and_i8.wat b/tests/integration/expected/and_i8.wat deleted file mode 100644 index 8f3374696..000000000 --- a/tests/integration/expected/and_i8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_u16.hir b/tests/integration/expected/and_u16.hir deleted file mode 100644 index 109333667..000000000 --- a/tests/integration/expected/and_u16.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = band v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/and_u16.masm b/tests/integration/expected/and_u16.masm deleted file mode 100644 index 8ff0c42ac..000000000 --- a/tests/integration/expected/and_u16.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/and_u16.wat b/tests/integration/expected/and_u16.wat deleted file mode 100644 index 8f3374696..000000000 --- a/tests/integration/expected/and_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_u32.hir b/tests/integration/expected/and_u32.hir deleted file mode 100644 index 109333667..000000000 --- a/tests/integration/expected/and_u32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = band v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/and_u32.masm b/tests/integration/expected/and_u32.masm deleted file mode 100644 index 8ff0c42ac..000000000 --- a/tests/integration/expected/and_u32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/and_u32.wat b/tests/integration/expected/and_u32.wat deleted file mode 100644 index 8f3374696..000000000 --- a/tests/integration/expected/and_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_u64.hir b/tests/integration/expected/and_u64.hir deleted file mode 100644 index 9679fe017..000000000 --- a/tests/integration/expected/and_u64.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = band v1, v0 : i64; - ret v3; -} diff --git a/tests/integration/expected/and_u64.masm b/tests/integration/expected/and_u64.masm deleted file mode 100644 index 976a751d7..000000000 --- a/tests/integration/expected/and_u64.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - exec.checked_and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/and_u64.wat b/tests/integration/expected/and_u64.wat deleted file mode 100644 index ba86570b6..000000000 --- a/tests/integration/expected/and_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 1 - local.get 0 - i64.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/and_u8.hir b/tests/integration/expected/and_u8.hir deleted file mode 100644 index 109333667..000000000 --- a/tests/integration/expected/and_u8.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = band v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/and_u8.masm b/tests/integration/expected/and_u8.masm deleted file mode 100644 index 8ff0c42ac..000000000 --- a/tests/integration/expected/and_u8.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/and_u8.wat b/tests/integration/expected/and_u8.wat deleted file mode 100644 index 8f3374696..000000000 --- a/tests/integration/expected/and_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/components/adder_wasm_component.wat b/tests/integration/expected/components/adder_wasm_component.wat deleted file mode 100644 index f62be4cde..000000000 --- a/tests/integration/expected/components/adder_wasm_component.wat +++ /dev/null @@ -1,6103 +0,0 @@ -(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) (result i32))) - (type (;4;) (func (param i32))) - (type (;5;) (func (param i32 i32))) - (type (;6;) (func (param i32 i32 i32) (result i32))) - (func $__wasm_call_ctors (;0;) (type 0)) - (func $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) - (local i32) - local.get 0 - local.get 1 - call $__rdl_alloc - local.set 2 - local.get 2 - return - ) - (func $__rust_realloc (;3;) (type 2) (param i32 i32 i32 i32) (result i32) - (local i32) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - call $__rdl_realloc - local.set 4 - local.get 4 - return - ) - (func $wit_bindgen::rt::run_ctors_once (;4;) (type 0) - block ;; label = @1 - i32.const 0 - i32.load8_u offset=1048577 - br_if 0 (;@1;) - call $__wasm_call_ctors - i32.const 0 - i32.const 1 - i32.store8 offset=1048577 - end - ) - (func $cabi_realloc (;5;) (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=1048576 - 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 - local.get 3 - call $__rust_realloc - local.set 2 - end - local.get 2 - br_if 0 (;@1;) - unreachable - unreachable - end - local.get 2 - ) - (func $__rdl_alloc (;6;) (type 1) (param i32 i32) (result i32) - block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.const 8 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - local.get 0 - i32.le_u - br_if 1 (;@1;) - end - local.get 1 - local.get 0 - call $aligned_alloc - return - end - local.get 0 - call $malloc - ) - (func $__rdl_realloc (;7;) (type 2) (param i32 i32 i32 i32) (result i32) - block ;; label = @1 - block ;; label = @2 - local.get 2 - i32.const 8 - i32.gt_u - br_if 0 (;@2;) - local.get 2 - local.get 3 - i32.le_u - br_if 1 (;@1;) - end - block ;; label = @2 - local.get 2 - local.get 3 - call $aligned_alloc - local.tee 2 - br_if 0 (;@2;) - i32.const 0 - return - end - local.get 2 - local.get 0 - local.get 1 - local.get 3 - local.get 1 - local.get 3 - i32.lt_u - select - call $memcpy - local.set 3 - local.get 0 - call $free - local.get 3 - return - end - local.get 0 - local.get 3 - call $realloc - ) - (func $malloc (;8;) (type 3) (param i32) (result i32) - local.get 0 - call $dlmalloc - ) - (func $dlmalloc (;9;) (type 3) (param i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - global.get $__stack_pointer - i32.const 16 - i32.sub - local.tee 1 - global.set $__stack_pointer - block ;; label = @1 - i32.const 0 - i32.load offset=1048604 - local.tee 2 - br_if 0 (;@1;) - block ;; label = @2 - block ;; label = @3 - i32.const 0 - i32.load offset=1049052 - local.tee 3 - i32.eqz - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1049056 - local.set 4 - br 1 (;@2;) - end - i32.const 0 - i64.const -1 - i64.store offset=1049064 align=4 - i32.const 0 - i64.const 281474976776192 - i64.store offset=1049056 align=4 - i32.const 0 - local.get 1 - i32.const 8 - i32.add - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - local.tee 3 - i32.store offset=1049052 - i32.const 0 - i32.const 0 - i32.store offset=1049072 - i32.const 0 - i32.const 0 - i32.store offset=1049024 - i32.const 65536 - local.set 4 - end - i32.const 0 - local.set 2 - i32.const 1114112 - i32.const 1049088 - local.get 4 - i32.add - i32.const -1 - i32.add - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.const 1114112 - select - i32.const 1049088 - i32.sub - local.tee 5 - i32.const 89 - i32.lt_u - br_if 0 (;@1;) - i32.const 0 - local.set 4 - i32.const 0 - local.get 5 - i32.store offset=1049032 - i32.const 0 - i32.const 1049088 - i32.store offset=1049028 - i32.const 0 - i32.const 1049088 - i32.store offset=1048596 - i32.const 0 - local.get 3 - i32.store offset=1048616 - i32.const 0 - i32.const -1 - i32.store offset=1048612 - loop ;; label = @2 - local.get 4 - i32.const 1048640 - i32.add - local.get 4 - i32.const 1048628 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 4 - i32.const 1048620 - i32.add - local.tee 6 - i32.store - local.get 4 - i32.const 1048632 - i32.add - local.get 6 - i32.store - local.get 4 - i32.const 1048648 - i32.add - local.get 4 - i32.const 1048636 - i32.add - local.tee 6 - i32.store - local.get 6 - local.get 3 - i32.store - local.get 4 - i32.const 1048656 - i32.add - local.get 4 - i32.const 1048644 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 6 - i32.store - local.get 4 - i32.const 1048652 - i32.add - local.get 3 - i32.store - local.get 4 - i32.const 32 - i32.add - local.tee 4 - i32.const 256 - i32.ne - br_if 0 (;@2;) - end - i32.const 1049088 - i32.const -8 - i32.const 1049088 - i32.sub - i32.const 15 - i32.and - i32.const 0 - i32.const 1049088 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 2 - i32.const 4 - i32.add - local.get 5 - i32.const -56 - i32.add - local.tee 3 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 2 - i32.store offset=1048604 - i32.const 1049088 - local.get 3 - i32.add - i32.const 56 - i32.store offset=4 - end - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - block ;; label = @12 - local.get 0 - i32.const 236 - i32.gt_u - br_if 0 (;@12;) - block ;; label = @13 - i32.const 0 - i32.load offset=1048580 - local.tee 7 - i32.const 16 - local.get 0 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 0 - i32.const 11 - i32.lt_u - select - local.tee 5 - i32.const 3 - i32.shr_u - local.tee 3 - i32.shr_u - local.tee 4 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@13;) - block ;; label = @14 - block ;; label = @15 - local.get 4 - i32.const 1 - i32.and - local.get 3 - i32.or - i32.const 1 - i32.xor - local.tee 6 - i32.const 3 - i32.shl - local.tee 3 - i32.const 1048620 - i32.add - local.tee 4 - local.get 3 - i32.const 1048628 - i32.add - i32.load - local.tee 3 - i32.load offset=8 - local.tee 5 - i32.ne - br_if 0 (;@15;) - i32.const 0 - local.get 7 - i32.const -2 - local.get 6 - i32.rotl - i32.and - i32.store offset=1048580 - br 1 (;@14;) - end - local.get 4 - local.get 5 - i32.store offset=8 - local.get 5 - local.get 4 - i32.store offset=12 - end - local.get 3 - i32.const 8 - i32.add - local.set 4 - local.get 3 - local.get 6 - i32.const 3 - i32.shl - local.tee 6 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 6 - i32.add - local.tee 3 - local.get 3 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 12 (;@1;) - end - local.get 5 - i32.const 0 - i32.load offset=1048588 - local.tee 8 - i32.le_u - br_if 1 (;@11;) - block ;; label = @13 - local.get 4 - i32.eqz - br_if 0 (;@13;) - block ;; label = @14 - block ;; label = @15 - local.get 4 - local.get 3 - i32.shl - i32.const 2 - local.get 3 - i32.shl - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.or - i32.and - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.ctz - local.tee 3 - i32.const 3 - i32.shl - local.tee 4 - i32.const 1048620 - i32.add - local.tee 6 - local.get 4 - i32.const 1048628 - i32.add - i32.load - local.tee 4 - i32.load offset=8 - local.tee 0 - i32.ne - br_if 0 (;@15;) - i32.const 0 - local.get 7 - i32.const -2 - local.get 3 - i32.rotl - i32.and - local.tee 7 - i32.store offset=1048580 - br 1 (;@14;) - end - local.get 6 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 6 - i32.store offset=12 - end - local.get 4 - local.get 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - i32.const 3 - i32.shl - local.tee 3 - i32.add - local.get 3 - local.get 5 - i32.sub - local.tee 6 - i32.store - local.get 4 - local.get 5 - i32.add - local.tee 0 - local.get 6 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @14 - local.get 8 - i32.eqz - br_if 0 (;@14;) - local.get 8 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 5 - i32.const 0 - i32.load offset=1048600 - local.set 3 - block ;; label = @15 - block ;; label = @16 - local.get 7 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 9 - i32.and - br_if 0 (;@16;) - i32.const 0 - local.get 7 - local.get 9 - i32.or - i32.store offset=1048580 - local.get 5 - local.set 9 - br 1 (;@15;) - end - local.get 5 - i32.load offset=8 - local.set 9 - end - local.get 9 - local.get 3 - i32.store offset=12 - local.get 5 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 5 - i32.store offset=12 - local.get 3 - local.get 9 - i32.store offset=8 - end - local.get 4 - i32.const 8 - i32.add - local.set 4 - i32.const 0 - local.get 0 - i32.store offset=1048600 - i32.const 0 - local.get 6 - i32.store offset=1048588 - br 12 (;@1;) - end - i32.const 0 - i32.load offset=1048584 - local.tee 10 - i32.eqz - br_if 1 (;@11;) - local.get 10 - i32.const 0 - local.get 10 - i32.sub - i32.and - i32.ctz - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.tee 0 - i32.load offset=4 - i32.const -8 - i32.and - local.get 5 - i32.sub - local.set 3 - local.get 0 - local.set 6 - block ;; label = @13 - loop ;; label = @14 - block ;; label = @15 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@15;) - local.get 6 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 2 (;@13;) - end - local.get 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 5 - i32.sub - local.tee 6 - local.get 3 - local.get 6 - local.get 3 - i32.lt_u - local.tee 6 - select - local.set 3 - local.get 4 - local.get 0 - local.get 6 - select - local.set 0 - local.get 4 - local.set 6 - br 0 (;@14;) - end - end - local.get 0 - i32.load offset=24 - local.set 11 - block ;; label = @13 - local.get 0 - i32.load offset=12 - local.tee 9 - local.get 0 - i32.eq - br_if 0 (;@13;) - local.get 0 - i32.load offset=8 - local.tee 4 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 9 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 9 - i32.store offset=12 - br 11 (;@2;) - end - block ;; label = @13 - local.get 0 - i32.const 20 - i32.add - local.tee 6 - i32.load - local.tee 4 - br_if 0 (;@13;) - local.get 0 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 3 (;@10;) - local.get 0 - i32.const 16 - i32.add - local.set 6 - end - loop ;; label = @13 - local.get 6 - local.set 2 - local.get 4 - local.tee 9 - i32.const 20 - i32.add - local.tee 6 - i32.load - local.tee 4 - br_if 0 (;@13;) - local.get 9 - i32.const 16 - i32.add - local.set 6 - local.get 9 - i32.load offset=16 - local.tee 4 - br_if 0 (;@13;) - end - local.get 2 - i32.const 0 - i32.store - br 10 (;@2;) - end - i32.const -1 - local.set 5 - local.get 0 - i32.const -65 - i32.gt_u - br_if 0 (;@11;) - local.get 0 - i32.const 19 - i32.add - local.tee 4 - i32.const -16 - i32.and - local.set 5 - i32.const 0 - i32.load offset=1048584 - local.tee 10 - i32.eqz - br_if 0 (;@11;) - i32.const 0 - local.set 8 - block ;; label = @12 - local.get 5 - i32.const 256 - i32.lt_u - br_if 0 (;@12;) - i32.const 31 - local.set 8 - local.get 5 - i32.const 16777215 - i32.gt_u - br_if 0 (;@12;) - local.get 5 - i32.const 38 - local.get 4 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 8 - end - i32.const 0 - local.get 5 - i32.sub - local.set 3 - block ;; label = @12 - block ;; label = @13 - block ;; label = @14 - block ;; label = @15 - local.get 8 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.tee 6 - br_if 0 (;@15;) - i32.const 0 - local.set 4 - i32.const 0 - local.set 9 - br 1 (;@14;) - end - i32.const 0 - local.set 4 - local.get 5 - i32.const 0 - i32.const 25 - local.get 8 - i32.const 1 - i32.shr_u - i32.sub - local.get 8 - i32.const 31 - i32.eq - select - i32.shl - local.set 0 - i32.const 0 - local.set 9 - loop ;; label = @15 - block ;; label = @16 - local.get 6 - i32.load offset=4 - i32.const -8 - i32.and - local.get 5 - i32.sub - local.tee 7 - local.get 3 - i32.ge_u - br_if 0 (;@16;) - local.get 7 - local.set 3 - local.get 6 - local.set 9 - local.get 7 - br_if 0 (;@16;) - i32.const 0 - local.set 3 - local.get 6 - local.set 9 - local.get 6 - local.set 4 - br 3 (;@13;) - end - local.get 4 - local.get 6 - i32.const 20 - i32.add - i32.load - local.tee 7 - local.get 7 - local.get 6 - local.get 0 - i32.const 29 - i32.shr_u - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - i32.load - local.tee 6 - i32.eq - select - local.get 4 - local.get 7 - select - local.set 4 - local.get 0 - i32.const 1 - i32.shl - local.set 0 - local.get 6 - br_if 0 (;@15;) - end - end - block ;; label = @14 - local.get 4 - local.get 9 - i32.or - br_if 0 (;@14;) - i32.const 0 - local.set 9 - i32.const 2 - local.get 8 - i32.shl - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.or - local.get 10 - i32.and - local.tee 4 - i32.eqz - br_if 3 (;@11;) - local.get 4 - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.ctz - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.set 4 - end - local.get 4 - i32.eqz - br_if 1 (;@12;) - end - loop ;; label = @13 - local.get 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 5 - i32.sub - local.tee 7 - local.get 3 - i32.lt_u - local.set 0 - block ;; label = @14 - local.get 4 - i32.load offset=16 - local.tee 6 - br_if 0 (;@14;) - local.get 4 - i32.const 20 - i32.add - i32.load - local.set 6 - end - local.get 7 - local.get 3 - local.get 0 - select - local.set 3 - local.get 4 - local.get 9 - local.get 0 - select - local.set 9 - local.get 6 - local.set 4 - local.get 6 - br_if 0 (;@13;) - end - end - local.get 9 - i32.eqz - br_if 0 (;@11;) - local.get 3 - i32.const 0 - i32.load offset=1048588 - local.get 5 - i32.sub - i32.ge_u - br_if 0 (;@11;) - local.get 9 - i32.load offset=24 - local.set 2 - block ;; label = @12 - local.get 9 - i32.load offset=12 - local.tee 0 - local.get 9 - i32.eq - br_if 0 (;@12;) - local.get 9 - i32.load offset=8 - local.tee 4 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 0 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 0 - i32.store offset=12 - br 9 (;@3;) - end - block ;; label = @12 - local.get 9 - i32.const 20 - i32.add - local.tee 6 - i32.load - local.tee 4 - br_if 0 (;@12;) - local.get 9 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 3 (;@9;) - local.get 9 - i32.const 16 - i32.add - local.set 6 - end - loop ;; label = @12 - local.get 6 - local.set 7 - local.get 4 - local.tee 0 - i32.const 20 - i32.add - local.tee 6 - i32.load - local.tee 4 - br_if 0 (;@12;) - local.get 0 - i32.const 16 - i32.add - local.set 6 - local.get 0 - i32.load offset=16 - local.tee 4 - br_if 0 (;@12;) - end - local.get 7 - i32.const 0 - i32.store - br 8 (;@3;) - end - block ;; label = @11 - i32.const 0 - i32.load offset=1048588 - local.tee 4 - local.get 5 - i32.lt_u - br_if 0 (;@11;) - i32.const 0 - i32.load offset=1048600 - local.set 3 - block ;; label = @12 - block ;; label = @13 - local.get 4 - local.get 5 - i32.sub - local.tee 6 - i32.const 16 - i32.lt_u - br_if 0 (;@13;) - local.get 3 - local.get 5 - i32.add - local.tee 0 - local.get 6 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 4 - i32.add - local.get 6 - i32.store - local.get 3 - local.get 5 - i32.const 3 - i32.or - i32.store offset=4 - br 1 (;@12;) - end - local.get 3 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 0 - i32.const 0 - local.set 6 - end - i32.const 0 - local.get 6 - i32.store offset=1048588 - i32.const 0 - local.get 0 - i32.store offset=1048600 - local.get 3 - i32.const 8 - i32.add - local.set 4 - br 10 (;@1;) - end - block ;; label = @11 - i32.const 0 - i32.load offset=1048592 - local.tee 6 - local.get 5 - i32.le_u - br_if 0 (;@11;) - local.get 2 - local.get 5 - i32.add - local.tee 4 - local.get 6 - local.get 5 - i32.sub - local.tee 3 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.get 4 - i32.store offset=1048604 - i32.const 0 - local.get 3 - i32.store offset=1048592 - local.get 2 - local.get 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 2 - i32.const 8 - i32.add - local.set 4 - br 10 (;@1;) - end - block ;; label = @11 - block ;; label = @12 - i32.const 0 - i32.load offset=1049052 - i32.eqz - br_if 0 (;@12;) - i32.const 0 - i32.load offset=1049060 - local.set 3 - br 1 (;@11;) - end - i32.const 0 - i64.const -1 - i64.store offset=1049064 align=4 - i32.const 0 - i64.const 281474976776192 - i64.store offset=1049056 align=4 - i32.const 0 - local.get 1 - i32.const 12 - i32.add - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - i32.store offset=1049052 - i32.const 0 - i32.const 0 - i32.store offset=1049072 - i32.const 0 - i32.const 0 - i32.store offset=1049024 - i32.const 65536 - local.set 3 - end - i32.const 0 - local.set 4 - block ;; label = @11 - local.get 3 - local.get 5 - i32.const 71 - i32.add - local.tee 8 - i32.add - local.tee 0 - i32.const 0 - local.get 3 - i32.sub - local.tee 7 - i32.and - local.tee 9 - local.get 5 - i32.gt_u - br_if 0 (;@11;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 10 (;@1;) - end - block ;; label = @11 - i32.const 0 - i32.load offset=1049020 - local.tee 4 - i32.eqz - br_if 0 (;@11;) - block ;; label = @12 - i32.const 0 - i32.load offset=1049012 - local.tee 3 - local.get 9 - i32.add - local.tee 10 - local.get 3 - i32.le_u - br_if 0 (;@12;) - local.get 10 - local.get 4 - i32.le_u - br_if 1 (;@11;) - end - i32.const 0 - local.set 4 - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 10 (;@1;) - end - i32.const 0 - i32.load8_u offset=1049024 - i32.const 4 - i32.and - br_if 4 (;@6;) - block ;; label = @11 - block ;; label = @12 - block ;; label = @13 - local.get 2 - i32.eqz - br_if 0 (;@13;) - i32.const 1049028 - local.set 4 - loop ;; label = @14 - block ;; label = @15 - local.get 4 - i32.load - local.tee 3 - local.get 2 - i32.gt_u - br_if 0 (;@15;) - local.get 3 - local.get 4 - i32.load offset=4 - i32.add - local.get 2 - i32.gt_u - br_if 3 (;@12;) - end - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@14;) - end - end - i32.const 0 - call $sbrk - local.tee 0 - i32.const -1 - i32.eq - br_if 5 (;@7;) - local.get 9 - local.set 7 - block ;; label = @13 - i32.const 0 - i32.load offset=1049056 - local.tee 4 - i32.const -1 - i32.add - local.tee 3 - local.get 0 - i32.and - i32.eqz - br_if 0 (;@13;) - local.get 9 - local.get 0 - i32.sub - local.get 3 - local.get 0 - i32.add - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.add - local.set 7 - end - local.get 7 - local.get 5 - i32.le_u - br_if 5 (;@7;) - local.get 7 - i32.const 2147483646 - i32.gt_u - br_if 5 (;@7;) - block ;; label = @13 - i32.const 0 - i32.load offset=1049020 - local.tee 4 - i32.eqz - br_if 0 (;@13;) - i32.const 0 - i32.load offset=1049012 - local.tee 3 - local.get 7 - i32.add - local.tee 6 - local.get 3 - i32.le_u - br_if 6 (;@7;) - local.get 6 - local.get 4 - i32.gt_u - br_if 6 (;@7;) - end - local.get 7 - call $sbrk - local.tee 4 - local.get 0 - i32.ne - br_if 1 (;@11;) - br 7 (;@5;) - end - local.get 0 - local.get 6 - i32.sub - local.get 7 - i32.and - local.tee 7 - i32.const 2147483646 - i32.gt_u - br_if 4 (;@7;) - local.get 7 - call $sbrk - local.tee 0 - local.get 4 - i32.load - local.get 4 - i32.load offset=4 - i32.add - i32.eq - br_if 3 (;@8;) - local.get 0 - local.set 4 - end - block ;; label = @11 - local.get 4 - i32.const -1 - i32.eq - br_if 0 (;@11;) - local.get 5 - i32.const 72 - i32.add - local.get 7 - i32.le_u - br_if 0 (;@11;) - block ;; label = @12 - local.get 8 - local.get 7 - i32.sub - i32.const 0 - i32.load offset=1049060 - local.tee 3 - i32.add - i32.const 0 - local.get 3 - i32.sub - i32.and - local.tee 3 - i32.const 2147483646 - i32.le_u - br_if 0 (;@12;) - local.get 4 - local.set 0 - br 7 (;@5;) - end - block ;; label = @12 - local.get 3 - call $sbrk - i32.const -1 - i32.eq - br_if 0 (;@12;) - local.get 3 - local.get 7 - i32.add - local.set 7 - local.get 4 - local.set 0 - br 7 (;@5;) - end - i32.const 0 - local.get 7 - i32.sub - call $sbrk - drop - br 4 (;@7;) - end - local.get 4 - local.set 0 - local.get 4 - i32.const -1 - i32.ne - br_if 5 (;@5;) - br 3 (;@7;) - end - i32.const 0 - local.set 9 - br 7 (;@2;) - end - i32.const 0 - local.set 0 - br 5 (;@3;) - end - local.get 0 - i32.const -1 - i32.ne - br_if 2 (;@5;) - end - i32.const 0 - i32.const 0 - i32.load offset=1049024 - i32.const 4 - i32.or - i32.store offset=1049024 - end - local.get 9 - i32.const 2147483646 - i32.gt_u - br_if 1 (;@4;) - local.get 9 - call $sbrk - local.set 0 - i32.const 0 - call $sbrk - local.set 4 - local.get 0 - i32.const -1 - i32.eq - br_if 1 (;@4;) - local.get 4 - i32.const -1 - i32.eq - br_if 1 (;@4;) - local.get 0 - local.get 4 - i32.ge_u - br_if 1 (;@4;) - local.get 4 - local.get 0 - i32.sub - local.tee 7 - local.get 5 - i32.const 56 - i32.add - i32.le_u - br_if 1 (;@4;) - end - i32.const 0 - i32.const 0 - i32.load offset=1049012 - local.get 7 - i32.add - local.tee 4 - i32.store offset=1049012 - block ;; label = @5 - local.get 4 - i32.const 0 - i32.load offset=1049016 - i32.le_u - br_if 0 (;@5;) - i32.const 0 - local.get 4 - i32.store offset=1049016 - end - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - i32.const 0 - i32.load offset=1048604 - local.tee 3 - i32.eqz - br_if 0 (;@8;) - i32.const 1049028 - local.set 4 - loop ;; label = @9 - local.get 0 - local.get 4 - i32.load - local.tee 6 - local.get 4 - i32.load offset=4 - local.tee 9 - i32.add - i32.eq - br_if 2 (;@7;) - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@9;) - br 3 (;@6;) - end - end - block ;; label = @8 - block ;; label = @9 - i32.const 0 - i32.load offset=1048596 - local.tee 4 - i32.eqz - br_if 0 (;@9;) - local.get 0 - local.get 4 - i32.ge_u - br_if 1 (;@8;) - end - i32.const 0 - local.get 0 - i32.store offset=1048596 - end - i32.const 0 - local.set 4 - i32.const 0 - local.get 7 - i32.store offset=1049032 - i32.const 0 - local.get 0 - i32.store offset=1049028 - i32.const 0 - i32.const -1 - i32.store offset=1048612 - i32.const 0 - i32.const 0 - i32.load offset=1049052 - i32.store offset=1048616 - i32.const 0 - i32.const 0 - i32.store offset=1049040 - loop ;; label = @8 - local.get 4 - i32.const 1048640 - i32.add - local.get 4 - i32.const 1048628 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 4 - i32.const 1048620 - i32.add - local.tee 6 - i32.store - local.get 4 - i32.const 1048632 - i32.add - local.get 6 - i32.store - local.get 4 - i32.const 1048648 - i32.add - local.get 4 - i32.const 1048636 - i32.add - local.tee 6 - i32.store - local.get 6 - local.get 3 - i32.store - local.get 4 - i32.const 1048656 - i32.add - local.get 4 - i32.const 1048644 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 6 - i32.store - local.get 4 - i32.const 1048652 - i32.add - local.get 3 - i32.store - local.get 4 - i32.const 32 - i32.add - local.tee 4 - i32.const 256 - i32.ne - br_if 0 (;@8;) - end - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 3 - local.get 7 - i32.const -56 - i32.add - local.tee 6 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 3 - i32.store offset=1048604 - local.get 0 - local.get 6 - i32.add - i32.const 56 - i32.store offset=4 - br 2 (;@5;) - end - local.get 4 - i32.load8_u offset=12 - i32.const 8 - i32.and - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.lt_u - br_if 0 (;@6;) - local.get 3 - local.get 0 - i32.ge_u - br_if 0 (;@6;) - local.get 3 - i32.const -8 - local.get 3 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 3 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 6 - i32.add - local.tee 0 - i32.const 0 - i32.load offset=1048592 - local.get 7 - i32.add - local.tee 2 - local.get 6 - i32.sub - local.tee 6 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 9 - local.get 7 - i32.add - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 6 - i32.store offset=1048592 - i32.const 0 - local.get 0 - i32.store offset=1048604 - local.get 3 - local.get 2 - i32.add - i32.const 56 - i32.store offset=4 - br 1 (;@5;) - end - block ;; label = @6 - local.get 0 - i32.const 0 - i32.load offset=1048596 - local.tee 9 - i32.ge_u - br_if 0 (;@6;) - i32.const 0 - local.get 0 - i32.store offset=1048596 - local.get 0 - local.set 9 - end - local.get 0 - local.get 7 - i32.add - local.set 6 - i32.const 1049028 - local.set 4 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - block ;; label = @12 - loop ;; label = @13 - local.get 4 - i32.load - local.get 6 - i32.eq - br_if 1 (;@12;) - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@13;) - br 2 (;@11;) - end - end - local.get 4 - i32.load8_u offset=12 - i32.const 8 - i32.and - i32.eqz - br_if 1 (;@10;) - end - i32.const 1049028 - local.set 4 - loop ;; label = @11 - block ;; label = @12 - local.get 4 - i32.load - local.tee 6 - local.get 3 - i32.gt_u - br_if 0 (;@12;) - local.get 6 - local.get 4 - i32.load offset=4 - i32.add - local.tee 6 - local.get 3 - i32.gt_u - br_if 3 (;@9;) - end - local.get 4 - i32.load offset=8 - local.set 4 - br 0 (;@11;) - end - end - local.get 4 - local.get 0 - i32.store - local.get 4 - local.get 4 - i32.load offset=4 - local.get 7 - i32.add - i32.store offset=4 - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - i32.add - local.tee 2 - local.get 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 6 - i32.const -8 - local.get 6 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 6 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - i32.add - local.tee 7 - local.get 2 - local.get 5 - i32.add - local.tee 5 - i32.sub - local.set 4 - block ;; label = @10 - local.get 7 - local.get 3 - i32.ne - br_if 0 (;@10;) - i32.const 0 - local.get 5 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 4 - i32.add - local.tee 4 - i32.store offset=1048592 - local.get 5 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - br 3 (;@7;) - end - block ;; label = @10 - local.get 7 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@10;) - i32.const 0 - local.get 5 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 4 - i32.add - local.tee 4 - i32.store offset=1048588 - local.get 5 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 5 - local.get 4 - i32.add - local.get 4 - i32.store - br 3 (;@7;) - end - block ;; label = @10 - local.get 7 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 1 - i32.ne - br_if 0 (;@10;) - local.get 3 - i32.const -8 - i32.and - local.set 8 - block ;; label = @11 - block ;; label = @12 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@12;) - local.get 7 - i32.load offset=8 - local.tee 6 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 9 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 0 - i32.eq - drop - block ;; label = @13 - local.get 7 - i32.load offset=12 - local.tee 3 - local.get 6 - i32.ne - br_if 0 (;@13;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 9 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@11;) - end - local.get 3 - local.get 0 - i32.eq - drop - local.get 3 - local.get 6 - i32.store offset=8 - local.get 6 - local.get 3 - i32.store offset=12 - br 1 (;@11;) - end - local.get 7 - i32.load offset=24 - local.set 10 - block ;; label = @12 - block ;; label = @13 - local.get 7 - i32.load offset=12 - local.tee 0 - local.get 7 - i32.eq - br_if 0 (;@13;) - local.get 7 - i32.load offset=8 - local.tee 3 - local.get 9 - i32.lt_u - drop - local.get 0 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 0 - i32.store offset=12 - br 1 (;@12;) - end - block ;; label = @13 - local.get 7 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 6 - br_if 0 (;@13;) - local.get 7 - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 6 - br_if 0 (;@13;) - i32.const 0 - local.set 0 - br 1 (;@12;) - end - loop ;; label = @13 - local.get 3 - local.set 9 - local.get 6 - local.tee 0 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 6 - br_if 0 (;@13;) - local.get 0 - i32.const 16 - i32.add - local.set 3 - local.get 0 - i32.load offset=16 - local.tee 6 - br_if 0 (;@13;) - end - local.get 9 - i32.const 0 - i32.store - end - local.get 10 - i32.eqz - br_if 0 (;@11;) - block ;; label = @12 - block ;; label = @13 - local.get 7 - local.get 7 - i32.load offset=28 - local.tee 6 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@13;) - local.get 3 - local.get 0 - i32.store - local.get 0 - br_if 1 (;@12;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 6 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@11;) - end - local.get 10 - i32.const 16 - i32.const 20 - local.get 10 - i32.load offset=16 - local.get 7 - i32.eq - select - i32.add - local.get 0 - i32.store - local.get 0 - i32.eqz - br_if 1 (;@11;) - end - local.get 0 - local.get 10 - i32.store offset=24 - block ;; label = @12 - local.get 7 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@12;) - local.get 0 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 0 - i32.store offset=24 - end - local.get 7 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 0 (;@11;) - local.get 0 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 0 - i32.store offset=24 - end - local.get 8 - local.get 4 - i32.add - local.set 4 - local.get 7 - local.get 8 - i32.add - local.tee 7 - i32.load offset=4 - local.set 3 - end - local.get 7 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 5 - local.get 4 - i32.add - local.get 4 - i32.store - local.get 5 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @10 - local.get 4 - i32.const 255 - i32.gt_u - br_if 0 (;@10;) - local.get 4 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 3 - block ;; label = @11 - block ;; label = @12 - i32.const 0 - i32.load offset=1048580 - local.tee 6 - i32.const 1 - local.get 4 - i32.const 3 - i32.shr_u - i32.shl - local.tee 4 - i32.and - br_if 0 (;@12;) - i32.const 0 - local.get 6 - local.get 4 - i32.or - i32.store offset=1048580 - local.get 3 - local.set 4 - br 1 (;@11;) - end - local.get 3 - i32.load offset=8 - local.set 4 - end - local.get 4 - local.get 5 - i32.store offset=12 - local.get 3 - local.get 5 - i32.store offset=8 - local.get 5 - local.get 3 - i32.store offset=12 - local.get 5 - local.get 4 - i32.store offset=8 - br 3 (;@7;) - end - i32.const 31 - local.set 3 - block ;; label = @10 - local.get 4 - i32.const 16777215 - i32.gt_u - br_if 0 (;@10;) - local.get 4 - i32.const 38 - local.get 4 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 5 - local.get 3 - i32.store offset=28 - local.get 5 - i64.const 0 - i64.store offset=16 align=4 - local.get 3 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 6 - block ;; label = @10 - i32.const 0 - i32.load offset=1048584 - local.tee 0 - i32.const 1 - local.get 3 - i32.shl - local.tee 9 - i32.and - br_if 0 (;@10;) - local.get 6 - local.get 5 - i32.store - i32.const 0 - local.get 0 - local.get 9 - i32.or - i32.store offset=1048584 - local.get 5 - local.get 6 - i32.store offset=24 - local.get 5 - local.get 5 - i32.store offset=8 - local.get 5 - local.get 5 - i32.store offset=12 - br 3 (;@7;) - end - local.get 4 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 6 - i32.load - local.set 0 - loop ;; label = @10 - local.get 0 - local.tee 6 - i32.load offset=4 - i32.const -8 - i32.and - local.get 4 - i32.eq - br_if 2 (;@8;) - local.get 3 - i32.const 29 - i32.shr_u - local.set 0 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 6 - local.get 0 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 9 - i32.load - local.tee 0 - br_if 0 (;@10;) - end - local.get 9 - local.get 5 - i32.store - local.get 5 - local.get 6 - i32.store offset=24 - local.get 5 - local.get 5 - i32.store offset=12 - local.get 5 - local.get 5 - i32.store offset=8 - br 2 (;@7;) - end - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 2 - local.get 7 - i32.const -56 - i32.add - local.tee 9 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 9 - i32.add - i32.const 56 - i32.store offset=4 - local.get 3 - local.get 6 - i32.const 55 - local.get 6 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 6 - i32.const -55 - i32.add - i32.const 15 - i32.and - select - i32.add - i32.const -63 - i32.add - local.tee 9 - local.get 9 - local.get 3 - i32.const 16 - i32.add - i32.lt_u - select - local.tee 9 - i32.const 35 - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 2 - i32.store offset=1048604 - local.get 9 - i32.const 16 - i32.add - i32.const 0 - i64.load offset=1049036 align=4 - i64.store align=4 - local.get 9 - i32.const 0 - i64.load offset=1049028 align=4 - i64.store offset=8 align=4 - i32.const 0 - local.get 9 - i32.const 8 - i32.add - i32.store offset=1049036 - i32.const 0 - local.get 7 - i32.store offset=1049032 - i32.const 0 - local.get 0 - i32.store offset=1049028 - i32.const 0 - i32.const 0 - i32.store offset=1049040 - local.get 9 - i32.const 36 - i32.add - local.set 4 - loop ;; label = @9 - local.get 4 - i32.const 7 - i32.store - local.get 4 - i32.const 4 - i32.add - local.tee 4 - local.get 6 - i32.lt_u - br_if 0 (;@9;) - end - local.get 9 - local.get 3 - i32.eq - br_if 3 (;@5;) - local.get 9 - local.get 9 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - local.get 9 - local.get 9 - local.get 3 - i32.sub - local.tee 0 - i32.store - local.get 3 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @9 - local.get 0 - i32.const 255 - i32.gt_u - br_if 0 (;@9;) - local.get 0 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 4 - block ;; label = @10 - block ;; label = @11 - i32.const 0 - i32.load offset=1048580 - local.tee 6 - i32.const 1 - local.get 0 - i32.const 3 - i32.shr_u - i32.shl - local.tee 0 - i32.and - br_if 0 (;@11;) - i32.const 0 - local.get 6 - local.get 0 - i32.or - i32.store offset=1048580 - local.get 4 - local.set 6 - br 1 (;@10;) - end - local.get 4 - i32.load offset=8 - local.set 6 - end - local.get 6 - local.get 3 - i32.store offset=12 - local.get 4 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 4 - i32.store offset=12 - local.get 3 - local.get 6 - i32.store offset=8 - br 4 (;@5;) - end - i32.const 31 - local.set 4 - block ;; label = @9 - local.get 0 - i32.const 16777215 - i32.gt_u - br_if 0 (;@9;) - local.get 0 - i32.const 38 - local.get 0 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 4 - end - local.get 3 - local.get 4 - i32.store offset=28 - local.get 3 - i64.const 0 - i64.store offset=16 align=4 - local.get 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 6 - block ;; label = @9 - i32.const 0 - i32.load offset=1048584 - local.tee 9 - i32.const 1 - local.get 4 - i32.shl - local.tee 7 - i32.and - br_if 0 (;@9;) - local.get 6 - local.get 3 - i32.store - i32.const 0 - local.get 9 - local.get 7 - i32.or - i32.store offset=1048584 - local.get 3 - local.get 6 - i32.store offset=24 - local.get 3 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 3 - i32.store offset=12 - br 4 (;@5;) - end - local.get 0 - i32.const 0 - i32.const 25 - local.get 4 - i32.const 1 - i32.shr_u - i32.sub - local.get 4 - i32.const 31 - i32.eq - select - i32.shl - local.set 4 - local.get 6 - i32.load - local.set 9 - loop ;; label = @9 - local.get 9 - local.tee 6 - i32.load offset=4 - i32.const -8 - i32.and - local.get 0 - i32.eq - br_if 3 (;@6;) - local.get 4 - i32.const 29 - i32.shr_u - local.set 9 - local.get 4 - i32.const 1 - i32.shl - local.set 4 - local.get 6 - local.get 9 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 7 - i32.load - local.tee 9 - br_if 0 (;@9;) - end - local.get 7 - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - local.get 3 - local.get 3 - i32.store offset=12 - local.get 3 - local.get 3 - i32.store offset=8 - br 3 (;@5;) - end - local.get 6 - i32.load offset=8 - local.tee 4 - local.get 5 - i32.store offset=12 - local.get 6 - local.get 5 - i32.store offset=8 - local.get 5 - i32.const 0 - i32.store offset=24 - local.get 5 - local.get 6 - i32.store offset=12 - local.get 5 - local.get 4 - i32.store offset=8 - end - local.get 2 - i32.const 8 - i32.add - local.set 4 - br 5 (;@1;) - end - local.get 6 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.store offset=12 - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - i32.const 0 - i32.store offset=24 - local.get 3 - local.get 6 - i32.store offset=12 - local.get 3 - local.get 4 - i32.store offset=8 - end - i32.const 0 - i32.load offset=1048592 - local.tee 4 - local.get 5 - i32.le_u - br_if 0 (;@4;) - i32.const 0 - i32.load offset=1048604 - local.tee 3 - local.get 5 - i32.add - local.tee 6 - local.get 4 - local.get 5 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 6 - i32.store offset=1048604 - local.get 3 - local.get 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - i32.const 8 - i32.add - local.set 4 - br 3 (;@1;) - end - i32.const 0 - local.set 4 - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 2 (;@1;) - end - block ;; label = @3 - local.get 2 - i32.eqz - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 9 - local.get 9 - i32.load offset=28 - local.tee 6 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 4 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 4 - local.get 0 - i32.store - local.get 0 - br_if 1 (;@4;) - i32.const 0 - local.get 10 - i32.const -2 - local.get 6 - i32.rotl - i32.and - local.tee 10 - i32.store offset=1048584 - br 2 (;@3;) - end - local.get 2 - i32.const 16 - i32.const 20 - local.get 2 - i32.load offset=16 - local.get 9 - i32.eq - select - i32.add - local.get 0 - i32.store - local.get 0 - i32.eqz - br_if 1 (;@3;) - end - local.get 0 - local.get 2 - i32.store offset=24 - block ;; label = @4 - local.get 9 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 0 (;@4;) - local.get 0 - local.get 4 - i32.store offset=16 - local.get 4 - local.get 0 - i32.store offset=24 - end - local.get 9 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@3;) - local.get 0 - i32.const 20 - i32.add - local.get 4 - i32.store - local.get 4 - local.get 0 - i32.store offset=24 - end - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 15 - i32.gt_u - br_if 0 (;@4;) - local.get 9 - local.get 3 - local.get 5 - i32.add - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 9 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 1 (;@3;) - end - local.get 9 - local.get 5 - i32.add - local.tee 0 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 9 - local.get 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @4 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 3 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 4 - block ;; label = @5 - block ;; label = @6 - i32.const 0 - i32.load offset=1048580 - local.tee 6 - i32.const 1 - local.get 3 - i32.const 3 - i32.shr_u - i32.shl - local.tee 3 - i32.and - br_if 0 (;@6;) - i32.const 0 - local.get 6 - local.get 3 - i32.or - i32.store offset=1048580 - local.get 4 - local.set 3 - br 1 (;@5;) - end - local.get 4 - i32.load offset=8 - local.set 3 - end - local.get 3 - local.get 0 - i32.store offset=12 - local.get 4 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 4 - i32.store offset=12 - local.get 0 - local.get 3 - i32.store offset=8 - br 1 (;@3;) - end - i32.const 31 - local.set 4 - block ;; label = @4 - local.get 3 - i32.const 16777215 - i32.gt_u - br_if 0 (;@4;) - local.get 3 - i32.const 38 - local.get 3 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 4 - end - local.get 0 - local.get 4 - i32.store offset=28 - local.get 0 - i64.const 0 - i64.store offset=16 align=4 - local.get 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 6 - block ;; label = @4 - local.get 10 - i32.const 1 - local.get 4 - i32.shl - local.tee 5 - i32.and - br_if 0 (;@4;) - local.get 6 - local.get 0 - i32.store - i32.const 0 - local.get 10 - local.get 5 - i32.or - i32.store offset=1048584 - local.get 0 - local.get 6 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 0 - i32.store offset=12 - br 1 (;@3;) - end - local.get 3 - i32.const 0 - i32.const 25 - local.get 4 - i32.const 1 - i32.shr_u - i32.sub - local.get 4 - i32.const 31 - i32.eq - select - i32.shl - local.set 4 - local.get 6 - i32.load - local.set 5 - block ;; label = @4 - loop ;; label = @5 - local.get 5 - local.tee 6 - i32.load offset=4 - i32.const -8 - i32.and - local.get 3 - i32.eq - br_if 1 (;@4;) - local.get 4 - i32.const 29 - i32.shr_u - local.set 5 - local.get 4 - i32.const 1 - i32.shl - local.set 4 - local.get 6 - local.get 5 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 7 - i32.load - local.tee 5 - br_if 0 (;@5;) - end - local.get 7 - local.get 0 - i32.store - local.get 0 - local.get 6 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=12 - local.get 0 - local.get 0 - i32.store offset=8 - br 1 (;@3;) - end - local.get 6 - i32.load offset=8 - local.tee 4 - local.get 0 - i32.store offset=12 - local.get 6 - local.get 0 - i32.store offset=8 - local.get 0 - i32.const 0 - i32.store offset=24 - local.get 0 - local.get 6 - i32.store offset=12 - local.get 0 - local.get 4 - i32.store offset=8 - end - local.get 9 - i32.const 8 - i32.add - local.set 4 - br 1 (;@1;) - end - block ;; label = @2 - local.get 11 - i32.eqz - br_if 0 (;@2;) - block ;; label = @3 - block ;; label = @4 - local.get 0 - local.get 0 - i32.load offset=28 - local.tee 6 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 4 - i32.load - i32.ne - br_if 0 (;@4;) - local.get 4 - local.get 9 - i32.store - local.get 9 - br_if 1 (;@3;) - i32.const 0 - local.get 10 - i32.const -2 - local.get 6 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@2;) - end - local.get 11 - i32.const 16 - i32.const 20 - local.get 11 - i32.load offset=16 - local.get 0 - i32.eq - select - i32.add - local.get 9 - i32.store - local.get 9 - i32.eqz - br_if 1 (;@2;) - end - local.get 9 - local.get 11 - i32.store offset=24 - block ;; label = @3 - local.get 0 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 0 (;@3;) - local.get 9 - local.get 4 - i32.store offset=16 - local.get 4 - local.get 9 - i32.store offset=24 - end - local.get 0 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@2;) - local.get 9 - i32.const 20 - i32.add - local.get 4 - i32.store - local.get 4 - local.get 9 - i32.store offset=24 - end - block ;; label = @2 - block ;; label = @3 - local.get 3 - i32.const 15 - i32.gt_u - br_if 0 (;@3;) - local.get 0 - local.get 3 - local.get 5 - i32.add - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 1 (;@2;) - end - local.get 0 - local.get 5 - i32.add - local.tee 6 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 6 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @3 - local.get 8 - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 5 - i32.const 0 - i32.load offset=1048600 - local.set 4 - block ;; label = @4 - block ;; label = @5 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 9 - local.get 7 - i32.and - br_if 0 (;@5;) - i32.const 0 - local.get 9 - local.get 7 - i32.or - i32.store offset=1048580 - local.get 5 - local.set 9 - br 1 (;@4;) - end - local.get 5 - i32.load offset=8 - local.set 9 - end - local.get 9 - local.get 4 - i32.store offset=12 - local.get 5 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 5 - i32.store offset=12 - local.get 4 - local.get 9 - i32.store offset=8 - end - i32.const 0 - local.get 6 - i32.store offset=1048600 - i32.const 0 - local.get 3 - i32.store offset=1048588 - end - local.get 0 - i32.const 8 - i32.add - local.set 4 - end - local.get 1 - i32.const 16 - i32.add - global.set $__stack_pointer - local.get 4 - ) - (func $free (;10;) (type 4) (param i32) - local.get 0 - call $dlfree - ) - (func $dlfree (;11;) (type 4) (param i32) - (local i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 0 - i32.eqz - br_if 0 (;@1;) - local.get 0 - i32.const -8 - i32.add - local.tee 1 - local.get 0 - i32.const -4 - i32.add - i32.load - local.tee 2 - i32.const -8 - i32.and - local.tee 0 - i32.add - local.set 3 - block ;; label = @2 - local.get 2 - i32.const 1 - i32.and - br_if 0 (;@2;) - local.get 2 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 1 - local.get 1 - i32.load - local.tee 2 - i32.sub - local.tee 1 - i32.const 0 - i32.load offset=1048596 - local.tee 4 - i32.lt_u - br_if 1 (;@1;) - local.get 2 - local.get 0 - i32.add - local.set 0 - block ;; label = @3 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.eq - br_if 0 (;@3;) - block ;; label = @4 - local.get 2 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 1 - i32.load offset=8 - local.tee 4 - local.get 2 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @5 - local.get 1 - i32.load offset=12 - local.tee 2 - local.get 4 - i32.ne - br_if 0 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 3 (;@2;) - end - local.get 2 - local.get 6 - i32.eq - drop - local.get 2 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 2 - i32.store offset=12 - br 2 (;@2;) - end - local.get 1 - i32.load offset=24 - local.set 7 - block ;; label = @4 - block ;; label = @5 - local.get 1 - i32.load offset=12 - local.tee 6 - local.get 1 - i32.eq - br_if 0 (;@5;) - local.get 1 - i32.load offset=8 - local.tee 2 - local.get 4 - i32.lt_u - drop - local.get 6 - local.get 2 - i32.store offset=8 - local.get 2 - local.get 6 - i32.store offset=12 - br 1 (;@4;) - end - block ;; label = @5 - local.get 1 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - local.get 1 - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - i32.const 0 - local.set 6 - br 1 (;@4;) - end - loop ;; label = @5 - local.get 2 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - local.get 6 - i32.const 16 - i32.add - local.set 2 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@5;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 1 - local.get 1 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 2 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 2 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@4;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 3 (;@2;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 1 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 2 (;@2;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @4 - local.get 1 - i32.load offset=16 - local.tee 2 - i32.eqz - br_if 0 (;@4;) - local.get 6 - local.get 2 - i32.store offset=16 - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 1 - i32.load offset=20 - local.tee 2 - i32.eqz - br_if 1 (;@2;) - local.get 6 - i32.const 20 - i32.add - local.get 2 - i32.store - local.get 2 - local.get 6 - i32.store offset=24 - br 1 (;@2;) - end - local.get 3 - i32.load offset=4 - local.tee 2 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 0 (;@2;) - local.get 3 - local.get 2 - i32.const -2 - i32.and - i32.store offset=4 - i32.const 0 - local.get 0 - i32.store offset=1048588 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - return - end - local.get 1 - local.get 3 - i32.ge_u - br_if 0 (;@1;) - local.get 3 - i32.load offset=4 - local.tee 2 - i32.const 1 - i32.and - i32.eqz - br_if 0 (;@1;) - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 2 - i32.and - br_if 0 (;@3;) - block ;; label = @4 - local.get 3 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 1 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 0 - i32.add - local.tee 0 - i32.store offset=1048592 - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 3 (;@1;) - i32.const 0 - i32.const 0 - i32.store offset=1048588 - i32.const 0 - i32.const 0 - i32.store offset=1048600 - return - end - block ;; label = @4 - local.get 3 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 1 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 0 - i32.add - local.tee 0 - i32.store offset=1048588 - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - return - end - local.get 2 - i32.const -8 - i32.and - local.get 0 - i32.add - local.set 0 - block ;; label = @4 - block ;; label = @5 - local.get 2 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 3 - i32.load offset=8 - local.tee 4 - local.get 2 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @6 - local.get 3 - i32.load offset=12 - local.tee 2 - local.get 4 - i32.ne - br_if 0 (;@6;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@4;) - end - local.get 2 - local.get 6 - i32.eq - drop - local.get 2 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 2 - i32.store offset=12 - br 1 (;@4;) - end - local.get 3 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.load offset=12 - local.tee 6 - local.get 3 - i32.eq - br_if 0 (;@6;) - local.get 3 - i32.load offset=8 - local.tee 2 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 2 - i32.store offset=8 - local.get 2 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 3 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 3 - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 2 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 2 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 0 (;@4;) - block ;; label = @5 - block ;; label = @6 - local.get 3 - local.get 3 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 2 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 2 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@4;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 3 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 1 (;@4;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 3 - i32.load offset=16 - local.tee 2 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 2 - i32.store offset=16 - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 3 - i32.load offset=20 - local.tee 2 - i32.eqz - br_if 0 (;@4;) - local.get 6 - i32.const 20 - i32.add - local.get 2 - i32.store - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 1 (;@2;) - i32.const 0 - local.get 0 - i32.store offset=1048588 - return - end - local.get 3 - local.get 2 - i32.const -2 - i32.and - i32.store offset=4 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - end - block ;; label = @2 - local.get 0 - i32.const 255 - i32.gt_u - br_if 0 (;@2;) - local.get 0 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 2 - block ;; label = @3 - block ;; label = @4 - i32.const 0 - i32.load offset=1048580 - local.tee 4 - i32.const 1 - local.get 0 - i32.const 3 - i32.shr_u - i32.shl - local.tee 0 - i32.and - br_if 0 (;@4;) - i32.const 0 - local.get 4 - local.get 0 - i32.or - i32.store offset=1048580 - local.get 2 - local.set 0 - br 1 (;@3;) - end - local.get 2 - i32.load offset=8 - local.set 0 - end - local.get 0 - local.get 1 - i32.store offset=12 - local.get 2 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 2 - i32.store offset=12 - local.get 1 - local.get 0 - i32.store offset=8 - return - end - i32.const 31 - local.set 2 - block ;; label = @2 - local.get 0 - i32.const 16777215 - i32.gt_u - br_if 0 (;@2;) - local.get 0 - i32.const 38 - local.get 0 - i32.const 8 - i32.shr_u - i32.clz - local.tee 2 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 2 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 2 - end - local.get 1 - local.get 2 - i32.store offset=28 - local.get 1 - i64.const 0 - i64.store offset=16 align=4 - local.get 2 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 4 - block ;; label = @2 - block ;; label = @3 - i32.const 0 - i32.load offset=1048584 - local.tee 6 - i32.const 1 - local.get 2 - i32.shl - local.tee 3 - i32.and - br_if 0 (;@3;) - local.get 4 - local.get 1 - i32.store - i32.const 0 - local.get 6 - local.get 3 - i32.or - i32.store offset=1048584 - local.get 1 - local.get 4 - i32.store offset=24 - local.get 1 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 1 - i32.store offset=12 - br 1 (;@2;) - end - local.get 0 - i32.const 0 - i32.const 25 - local.get 2 - i32.const 1 - i32.shr_u - i32.sub - local.get 2 - i32.const 31 - i32.eq - select - i32.shl - local.set 2 - local.get 4 - i32.load - local.set 6 - block ;; label = @3 - loop ;; label = @4 - local.get 6 - local.tee 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 0 - i32.eq - br_if 1 (;@3;) - local.get 2 - i32.const 29 - i32.shr_u - local.set 6 - local.get 2 - i32.const 1 - i32.shl - local.set 2 - local.get 4 - local.get 6 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 6 - br_if 0 (;@4;) - end - local.get 3 - local.get 1 - i32.store - local.get 1 - local.get 4 - i32.store offset=24 - local.get 1 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 1 - i32.store offset=8 - br 1 (;@2;) - end - local.get 4 - i32.load offset=8 - local.tee 0 - local.get 1 - i32.store offset=12 - local.get 4 - local.get 1 - i32.store offset=8 - local.get 1 - i32.const 0 - i32.store offset=24 - local.get 1 - local.get 4 - i32.store offset=12 - local.get 1 - local.get 0 - i32.store offset=8 - end - i32.const 0 - i32.const 0 - i32.load offset=1048612 - i32.const -1 - i32.add - local.tee 1 - i32.const -1 - local.get 1 - select - i32.store offset=1048612 - end - ) - (func $realloc (;12;) (type 1) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 0 - br_if 0 (;@1;) - local.get 1 - call $dlmalloc - return - end - block ;; label = @1 - local.get 1 - i32.const -64 - i32.lt_u - br_if 0 (;@1;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const 0 - return - end - i32.const 16 - local.get 1 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.set 2 - local.get 0 - i32.const -4 - i32.add - local.tee 3 - i32.load - local.tee 4 - i32.const -8 - i32.and - local.set 5 - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 4 - i32.const 3 - i32.and - br_if 0 (;@3;) - local.get 2 - i32.const 256 - i32.lt_u - br_if 1 (;@2;) - local.get 5 - local.get 2 - i32.const 4 - i32.or - i32.lt_u - br_if 1 (;@2;) - local.get 5 - local.get 2 - i32.sub - i32.const 0 - i32.load offset=1049060 - i32.const 1 - i32.shl - i32.le_u - br_if 2 (;@1;) - br 1 (;@2;) - end - local.get 0 - i32.const -8 - i32.add - local.tee 6 - local.get 5 - i32.add - local.set 7 - block ;; label = @3 - local.get 5 - local.get 2 - i32.lt_u - br_if 0 (;@3;) - local.get 5 - local.get 2 - i32.sub - local.tee 1 - i32.const 16 - i32.lt_u - br_if 2 (;@1;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 2 - local.get 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 7 - local.get 7 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 1 - call $dispose_chunk - local.get 0 - return - end - block ;; label = @3 - local.get 7 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1048592 - local.get 5 - i32.add - local.tee 5 - local.get 2 - i32.le_u - br_if 1 (;@2;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - i32.const 0 - local.get 6 - local.get 2 - i32.add - local.tee 1 - i32.store offset=1048604 - i32.const 0 - local.get 5 - local.get 2 - i32.sub - local.tee 2 - i32.store offset=1048592 - local.get 1 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - block ;; label = @3 - local.get 7 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1048588 - local.get 5 - i32.add - local.tee 5 - local.get 2 - i32.lt_u - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 5 - local.get 2 - i32.sub - local.tee 1 - i32.const 16 - i32.lt_u - br_if 0 (;@5;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 2 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 6 - local.get 5 - i32.add - local.tee 5 - local.get 1 - i32.store - local.get 5 - local.get 5 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - br 1 (;@4;) - end - local.get 3 - local.get 4 - i32.const 1 - i32.and - local.get 5 - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 5 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 1 - i32.const 0 - local.set 2 - end - i32.const 0 - local.get 2 - i32.store offset=1048600 - i32.const 0 - local.get 1 - i32.store offset=1048588 - local.get 0 - return - end - local.get 7 - i32.load offset=4 - local.tee 8 - i32.const 2 - i32.and - br_if 0 (;@2;) - local.get 8 - i32.const -8 - i32.and - local.get 5 - i32.add - local.tee 9 - local.get 2 - i32.lt_u - br_if 0 (;@2;) - local.get 9 - local.get 2 - i32.sub - local.set 10 - block ;; label = @3 - block ;; label = @4 - local.get 8 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 7 - i32.load offset=8 - local.tee 1 - local.get 8 - i32.const 3 - i32.shr_u - local.tee 11 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 8 - i32.eq - drop - block ;; label = @5 - local.get 7 - i32.load offset=12 - local.tee 5 - local.get 1 - i32.ne - br_if 0 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 11 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@3;) - end - local.get 5 - local.get 8 - i32.eq - drop - local.get 5 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 5 - i32.store offset=12 - br 1 (;@3;) - end - local.get 7 - i32.load offset=24 - local.set 12 - block ;; label = @4 - block ;; label = @5 - local.get 7 - i32.load offset=12 - local.tee 8 - local.get 7 - i32.eq - br_if 0 (;@5;) - local.get 7 - i32.load offset=8 - local.tee 1 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 8 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 8 - i32.store offset=12 - br 1 (;@4;) - end - block ;; label = @5 - local.get 7 - i32.const 20 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - local.get 7 - i32.const 16 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - i32.const 0 - local.set 8 - br 1 (;@4;) - end - loop ;; label = @5 - local.get 1 - local.set 11 - local.get 5 - local.tee 8 - i32.const 20 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - local.get 8 - i32.const 16 - i32.add - local.set 1 - local.get 8 - i32.load offset=16 - local.tee 5 - br_if 0 (;@5;) - end - local.get 11 - i32.const 0 - i32.store - end - local.get 12 - i32.eqz - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 7 - local.get 7 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 1 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 1 - local.get 8 - i32.store - local.get 8 - br_if 1 (;@4;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@3;) - end - local.get 12 - i32.const 16 - i32.const 20 - local.get 12 - i32.load offset=16 - local.get 7 - i32.eq - select - i32.add - local.get 8 - i32.store - local.get 8 - i32.eqz - br_if 1 (;@3;) - end - local.get 8 - local.get 12 - i32.store offset=24 - block ;; label = @4 - local.get 7 - i32.load offset=16 - local.tee 1 - i32.eqz - br_if 0 (;@4;) - local.get 8 - local.get 1 - i32.store offset=16 - local.get 1 - local.get 8 - i32.store offset=24 - end - local.get 7 - i32.load offset=20 - local.tee 1 - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const 20 - i32.add - local.get 1 - i32.store - local.get 1 - local.get 8 - i32.store offset=24 - end - block ;; label = @3 - local.get 10 - i32.const 15 - i32.gt_u - br_if 0 (;@3;) - local.get 3 - local.get 4 - i32.const 1 - i32.and - local.get 9 - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 9 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 1 - local.get 10 - i32.const 3 - i32.or - i32.store offset=4 - local.get 6 - local.get 9 - i32.add - local.tee 2 - local.get 2 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 10 - call $dispose_chunk - local.get 0 - return - end - block ;; label = @2 - local.get 1 - call $dlmalloc - local.tee 2 - br_if 0 (;@2;) - i32.const 0 - return - end - local.get 2 - local.get 0 - i32.const -4 - i32.const -8 - local.get 3 - i32.load - local.tee 5 - i32.const 3 - i32.and - select - local.get 5 - i32.const -8 - i32.and - i32.add - local.tee 5 - local.get 1 - local.get 5 - local.get 1 - i32.lt_u - select - call $memcpy - local.set 1 - local.get 0 - call $dlfree - local.get 1 - local.set 0 - end - local.get 0 - ) - (func $dispose_chunk (;13;) (type 5) (param i32 i32) - (local i32 i32 i32 i32 i32 i32) - local.get 0 - local.get 1 - i32.add - local.set 2 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 1 - i32.and - br_if 0 (;@2;) - local.get 3 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 0 - i32.load - local.tee 3 - local.get 1 - i32.add - local.set 1 - block ;; label = @3 - block ;; label = @4 - local.get 0 - local.get 3 - i32.sub - local.tee 0 - i32.const 0 - i32.load offset=1048600 - i32.eq - br_if 0 (;@4;) - block ;; label = @5 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 0 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - local.get 0 - i32.load offset=12 - local.tee 3 - local.get 4 - i32.ne - br_if 2 (;@3;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 3 (;@2;) - end - local.get 0 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 0 - i32.load offset=12 - local.tee 6 - local.get 0 - i32.eq - br_if 0 (;@6;) - local.get 0 - i32.load offset=8 - local.tee 3 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 0 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 0 - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 3 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 3 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 2 (;@2;) - block ;; label = @5 - block ;; label = @6 - local.get 0 - local.get 0 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 4 (;@2;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 0 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 3 (;@2;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 0 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 0 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 2 (;@2;) - local.get 6 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - br 2 (;@2;) - end - local.get 2 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 1 (;@2;) - local.get 2 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - i32.const 0 - local.get 1 - i32.store offset=1048588 - local.get 2 - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - return - end - local.get 3 - local.get 6 - i32.eq - drop - local.get 3 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 3 - i32.store offset=12 - end - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.load offset=4 - local.tee 3 - i32.const 2 - i32.and - br_if 0 (;@3;) - block ;; label = @4 - local.get 2 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 0 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 1 - i32.add - local.tee 1 - i32.store offset=1048592 - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 3 (;@1;) - i32.const 0 - i32.const 0 - i32.store offset=1048588 - i32.const 0 - i32.const 0 - i32.store offset=1048600 - return - end - block ;; label = @4 - local.get 2 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 0 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 1 - i32.add - local.tee 1 - i32.store offset=1048588 - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - return - end - local.get 3 - i32.const -8 - i32.and - local.get 1 - i32.add - local.set 1 - block ;; label = @4 - block ;; label = @5 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 2 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @6 - local.get 2 - i32.load offset=12 - local.tee 3 - local.get 4 - i32.ne - br_if 0 (;@6;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@4;) - end - local.get 3 - local.get 6 - i32.eq - drop - local.get 3 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 3 - i32.store offset=12 - br 1 (;@4;) - end - local.get 2 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 2 - i32.load offset=12 - local.tee 6 - local.get 2 - i32.eq - br_if 0 (;@6;) - local.get 2 - i32.load offset=8 - local.tee 3 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 2 - i32.const 20 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - local.get 2 - i32.const 16 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 4 - local.set 5 - local.get 3 - local.tee 6 - i32.const 20 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 4 - local.get 6 - i32.load offset=16 - local.tee 3 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 0 (;@4;) - block ;; label = @5 - block ;; label = @6 - local.get 2 - local.get 2 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@4;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 2 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 1 (;@4;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 2 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 2 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 0 (;@4;) - local.get 6 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 1 (;@2;) - i32.const 0 - local.get 1 - i32.store offset=1048588 - return - end - local.get 2 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - end - block ;; label = @2 - local.get 1 - i32.const 255 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 3 - block ;; label = @3 - block ;; label = @4 - i32.const 0 - i32.load offset=1048580 - local.tee 4 - i32.const 1 - local.get 1 - i32.const 3 - i32.shr_u - i32.shl - local.tee 1 - i32.and - br_if 0 (;@4;) - i32.const 0 - local.get 4 - local.get 1 - i32.or - i32.store offset=1048580 - local.get 3 - local.set 1 - br 1 (;@3;) - end - local.get 3 - i32.load offset=8 - local.set 1 - end - local.get 1 - local.get 0 - i32.store offset=12 - local.get 3 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 3 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - return - end - i32.const 31 - local.set 3 - block ;; label = @2 - local.get 1 - i32.const 16777215 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.const 38 - local.get 1 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 0 - local.get 3 - i32.store offset=28 - local.get 0 - i64.const 0 - i64.store offset=16 align=4 - local.get 3 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 4 - block ;; label = @2 - i32.const 0 - i32.load offset=1048584 - local.tee 6 - i32.const 1 - local.get 3 - i32.shl - local.tee 2 - i32.and - br_if 0 (;@2;) - local.get 4 - local.get 0 - i32.store - i32.const 0 - local.get 6 - local.get 2 - i32.or - i32.store offset=1048584 - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 0 - i32.store offset=12 - return - end - local.get 1 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 4 - i32.load - local.set 6 - block ;; label = @2 - loop ;; label = @3 - local.get 6 - local.tee 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 1 - i32.eq - br_if 1 (;@2;) - local.get 3 - i32.const 29 - i32.shr_u - local.set 6 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 4 - local.get 6 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 6 - br_if 0 (;@3;) - end - local.get 2 - local.get 0 - i32.store - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=12 - local.get 0 - local.get 0 - i32.store offset=8 - return - end - local.get 4 - i32.load offset=8 - local.tee 1 - local.get 0 - i32.store offset=12 - local.get 4 - local.get 0 - i32.store offset=8 - local.get 0 - i32.const 0 - i32.store offset=24 - local.get 0 - local.get 4 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - end - ) - (func $internal_memalign (;14;) (type 1) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.const 16 - local.get 0 - i32.const 16 - i32.gt_u - select - local.tee 2 - local.get 2 - i32.const -1 - i32.add - i32.and - br_if 0 (;@2;) - local.get 2 - local.set 0 - br 1 (;@1;) - end - i32.const 32 - local.set 3 - loop ;; label = @2 - local.get 3 - local.tee 0 - i32.const 1 - i32.shl - local.set 3 - local.get 0 - local.get 2 - i32.lt_u - br_if 0 (;@2;) - end - end - block ;; label = @1 - i32.const -64 - local.get 0 - i32.sub - local.get 1 - i32.gt_u - br_if 0 (;@1;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const 0 - return - end - block ;; label = @1 - local.get 0 - i32.const 16 - local.get 1 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.tee 1 - i32.add - i32.const 12 - i32.add - call $dlmalloc - local.tee 3 - br_if 0 (;@1;) - i32.const 0 - return - end - local.get 3 - i32.const -8 - i32.add - local.set 2 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.const -1 - i32.add - local.get 3 - i32.and - br_if 0 (;@2;) - local.get 2 - local.set 0 - br 1 (;@1;) - end - local.get 3 - i32.const -4 - i32.add - local.tee 4 - i32.load - local.tee 5 - i32.const -8 - i32.and - local.get 3 - local.get 0 - i32.add - i32.const -1 - i32.add - i32.const 0 - local.get 0 - i32.sub - i32.and - i32.const -8 - i32.add - local.tee 3 - i32.const 0 - local.get 0 - local.get 3 - local.get 2 - i32.sub - i32.const 15 - i32.gt_u - select - i32.add - local.tee 0 - local.get 2 - i32.sub - local.tee 3 - i32.sub - local.set 6 - block ;; label = @2 - local.get 5 - i32.const 3 - i32.and - br_if 0 (;@2;) - local.get 0 - local.get 6 - i32.store offset=4 - local.get 0 - local.get 2 - i32.load - local.get 3 - i32.add - i32.store - br 1 (;@1;) - end - local.get 0 - local.get 6 - local.get 0 - i32.load offset=4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 0 - local.get 6 - i32.add - local.tee 6 - local.get 6 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - local.get 4 - i32.load - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 2 - local.get 3 - i32.add - local.tee 6 - local.get 6 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 3 - call $dispose_chunk - end - block ;; label = @1 - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@1;) - local.get 3 - i32.const -8 - i32.and - local.tee 2 - local.get 1 - i32.const 16 - i32.add - i32.le_u - br_if 0 (;@1;) - local.get 0 - local.get 1 - local.get 3 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.tee 3 - local.get 2 - local.get 1 - i32.sub - local.tee 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 2 - i32.add - local.tee 2 - local.get 2 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 1 - call $dispose_chunk - end - local.get 0 - i32.const 8 - i32.add - ) - (func $aligned_alloc (;15;) (type 1) (param i32 i32) (result i32) - block ;; label = @1 - local.get 0 - i32.const 16 - i32.gt_u - br_if 0 (;@1;) - local.get 1 - call $dlmalloc - return - end - local.get 0 - local.get 1 - call $internal_memalign - ) - (func $abort (;16;) (type 0) - unreachable - unreachable - ) - (func $sbrk (;17;) (type 3) (param i32) (result i32) - block ;; label = @1 - local.get 0 - br_if 0 (;@1;) - memory.size - i32.const 16 - i32.shl - return - end - block ;; label = @1 - local.get 0 - i32.const 65535 - i32.and - br_if 0 (;@1;) - local.get 0 - i32.const -1 - i32.le_s - br_if 0 (;@1;) - block ;; label = @2 - local.get 0 - i32.const 16 - i32.shr_u - memory.grow - local.tee 0 - i32.const -1 - i32.ne - br_if 0 (;@2;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const -1 - return - end - local.get 0 - i32.const 16 - i32.shl - return - end - call $abort - unreachable - ) - (func $memcpy (;18;) (type 6) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 32 - i32.gt_u - br_if 0 (;@3;) - local.get 1 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@2;) - local.get 2 - i32.eqz - br_if 1 (;@2;) - local.get 0 - local.get 1 - i32.load8_u - i32.store8 - local.get 2 - i32.const -1 - i32.add - local.set 3 - local.get 0 - i32.const 1 - i32.add - local.set 4 - local.get 1 - i32.const 1 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=1 - i32.store8 offset=1 - local.get 2 - i32.const -2 - i32.add - local.set 3 - local.get 0 - i32.const 2 - i32.add - local.set 4 - local.get 1 - i32.const 2 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=2 - i32.store8 offset=2 - local.get 2 - i32.const -3 - i32.add - local.set 3 - local.get 0 - i32.const 3 - i32.add - local.set 4 - local.get 1 - i32.const 3 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=3 - i32.store8 offset=3 - local.get 2 - i32.const -4 - i32.add - local.set 3 - local.get 0 - i32.const 4 - i32.add - local.set 4 - local.get 1 - i32.const 4 - i32.add - local.set 5 - br 2 (;@1;) - end - local.get 0 - local.get 1 - local.get 2 - memory.copy - local.get 0 - return - end - local.get 2 - local.set 3 - local.get 0 - local.set 4 - local.get 1 - local.set 5 - end - block ;; label = @1 - block ;; label = @2 - local.get 4 - i32.const 3 - i32.and - local.tee 2 - br_if 0 (;@2;) - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 16 - i32.ge_u - br_if 0 (;@4;) - local.get 3 - local.set 2 - br 1 (;@3;) - end - block ;; label = @4 - local.get 3 - i32.const -16 - i32.add - local.tee 2 - i32.const 16 - i32.and - br_if 0 (;@4;) - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 4 - local.get 5 - i64.load offset=8 align=4 - i64.store offset=8 align=4 - local.get 4 - i32.const 16 - i32.add - local.set 4 - local.get 5 - i32.const 16 - i32.add - local.set 5 - local.get 2 - local.set 3 - end - local.get 2 - i32.const 16 - i32.lt_u - br_if 0 (;@3;) - local.get 3 - local.set 2 - loop ;; label = @4 - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 4 - local.get 5 - i64.load offset=8 align=4 - i64.store offset=8 align=4 - local.get 4 - local.get 5 - i64.load offset=16 align=4 - i64.store offset=16 align=4 - local.get 4 - local.get 5 - i64.load offset=24 align=4 - i64.store offset=24 align=4 - local.get 4 - i32.const 32 - i32.add - local.set 4 - local.get 5 - i32.const 32 - i32.add - local.set 5 - local.get 2 - i32.const -32 - i32.add - local.tee 2 - i32.const 15 - i32.gt_u - br_if 0 (;@4;) - end - end - block ;; label = @3 - local.get 2 - i32.const 8 - i32.lt_u - br_if 0 (;@3;) - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 5 - i32.const 8 - i32.add - local.set 5 - local.get 4 - i32.const 8 - i32.add - local.set 4 - end - block ;; label = @3 - local.get 2 - i32.const 4 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 4 - local.get 5 - i32.load - i32.store - local.get 5 - i32.const 4 - i32.add - local.set 5 - local.get 4 - i32.const 4 - i32.add - local.set 4 - end - block ;; label = @3 - local.get 2 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 4 - local.get 5 - i32.load16_u align=1 - i32.store16 align=1 - local.get 4 - i32.const 2 - i32.add - local.set 4 - local.get 5 - i32.const 2 - i32.add - local.set 5 - end - local.get 2 - i32.const 1 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 4 - local.get 5 - i32.load8_u - i32.store8 - local.get 0 - return - end - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.const 32 - i32.lt_u - br_if 0 (;@6;) - block ;; label = @7 - block ;; label = @8 - local.get 2 - i32.const -1 - i32.add - br_table 3 (;@5;) 0 (;@8;) 1 (;@7;) 7 (;@1;) - end - local.get 4 - local.get 5 - i32.load - i32.store16 align=1 - local.get 4 - local.get 5 - i32.const 2 - i32.add - i32.load align=2 - i32.store offset=2 - local.get 4 - local.get 5 - i32.const 6 - i32.add - i64.load align=2 - i64.store offset=6 align=4 - local.get 4 - i32.const 18 - i32.add - local.set 2 - local.get 5 - i32.const 18 - i32.add - local.set 1 - i32.const 14 - local.set 6 - local.get 5 - i32.const 14 - i32.add - i32.load align=2 - local.set 5 - i32.const 14 - local.set 3 - br 3 (;@4;) - end - local.get 4 - local.get 5 - i32.load - i32.store8 - local.get 4 - local.get 5 - i32.const 1 - i32.add - i32.load align=1 - i32.store offset=1 - local.get 4 - local.get 5 - i32.const 5 - i32.add - i64.load align=1 - i64.store offset=5 align=4 - local.get 4 - i32.const 17 - i32.add - local.set 2 - local.get 5 - i32.const 17 - i32.add - local.set 1 - i32.const 13 - local.set 6 - local.get 5 - i32.const 13 - i32.add - i32.load align=1 - local.set 5 - i32.const 15 - local.set 3 - br 2 (;@4;) - end - block ;; label = @6 - block ;; label = @7 - local.get 3 - i32.const 16 - i32.ge_u - br_if 0 (;@7;) - local.get 4 - local.set 2 - local.get 5 - local.set 1 - br 1 (;@6;) - end - local.get 4 - local.get 5 - i32.load8_u - i32.store8 - local.get 4 - local.get 5 - i32.load offset=1 align=1 - i32.store offset=1 align=1 - local.get 4 - local.get 5 - i64.load offset=5 align=1 - i64.store offset=5 align=1 - local.get 4 - local.get 5 - i32.load16_u offset=13 align=1 - i32.store16 offset=13 align=1 - local.get 4 - local.get 5 - i32.load8_u offset=15 - i32.store8 offset=15 - local.get 4 - i32.const 16 - i32.add - local.set 2 - local.get 5 - i32.const 16 - i32.add - local.set 1 - end - local.get 3 - i32.const 8 - i32.and - br_if 2 (;@3;) - br 3 (;@2;) - end - local.get 4 - local.get 5 - i32.load - local.tee 2 - i32.store8 - local.get 4 - local.get 2 - i32.const 16 - i32.shr_u - i32.store8 offset=2 - local.get 4 - local.get 2 - i32.const 8 - i32.shr_u - i32.store8 offset=1 - local.get 4 - local.get 5 - i32.const 3 - i32.add - i32.load align=1 - i32.store offset=3 - local.get 4 - local.get 5 - i32.const 7 - i32.add - i64.load align=1 - i64.store offset=7 align=4 - local.get 4 - i32.const 19 - i32.add - local.set 2 - local.get 5 - i32.const 19 - i32.add - local.set 1 - i32.const 15 - local.set 6 - local.get 5 - i32.const 15 - i32.add - i32.load align=1 - local.set 5 - i32.const 13 - local.set 3 - end - local.get 4 - local.get 6 - i32.add - local.get 5 - i32.store - end - local.get 2 - local.get 1 - i64.load align=1 - i64.store align=1 - local.get 2 - i32.const 8 - i32.add - local.set 2 - local.get 1 - i32.const 8 - i32.add - local.set 1 - end - block ;; label = @2 - local.get 3 - i32.const 4 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - local.get 1 - i32.load align=1 - i32.store align=1 - local.get 2 - i32.const 4 - i32.add - local.set 2 - local.get 1 - i32.const 4 - i32.add - local.set 1 - end - block ;; label = @2 - local.get 3 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - local.get 1 - i32.load16_u align=1 - i32.store16 align=1 - local.get 2 - i32.const 2 - i32.add - local.set 2 - local.get 1 - i32.const 2 - i32.add - local.set 1 - end - local.get 3 - i32.const 1 - i32.and - i32.eqz - br_if 0 (;@1;) - local.get 2 - local.get 1 - i32.load8_u - i32.store8 - end - local.get 0 - ) - (table (;0;) 1 1 funcref) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (export "memory" (memory 0)) - (export "add" (func $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" s32) (param "b" s32) (result s32))) - (alias core export 0 "add" (core func (;1;))) - (func (;0;) (type 0) (canon lift (core func 1))) - (export (;1;) "add" (func 0)) -) \ No newline at end of file diff --git a/tests/integration/expected/components/bindings/adder_wasm_component_bindings.rs b/tests/integration/expected/components/bindings/adder_wasm_component_bindings.rs deleted file mode 100644 index 2bf8cc119..000000000 --- a/tests/integration/expected/components/bindings/adder_wasm_component_bindings.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Generated by `wit-bindgen` 0.16.0. DO NOT EDIT! -const _: () = { - - #[doc(hidden)] - #[export_name = "add"] - #[allow(non_snake_case)] - unsafe extern "C" fn __export_add(arg0: i32,arg1: i32,) -> i32 { - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - - // Before executing any other code, use this function to run all static - // constructors, if they have not yet been run. This is a hack required - // to work around wasi-libc ctors calling import functions to initialize - // the environment. - // - // This functionality will be removed once rust 1.69.0 is stable, at which - // point wasi-libc will no longer have this behavior. - // - // See - // https://github.com/bytecodealliance/preview2-prototyping/issues/99 - // for more details. - #[cfg(target_arch="wasm32")] - ::cargo_component_bindings::rt::run_ctors_once(); - - let result0 = <_GuestImpl as Guest>::add(arg0, arg1); - ::cargo_component_bindings::rt::as_i32(result0) - } -}; -use super::Component as _GuestImpl; -pub trait Guest { - fn add(a: i32,b: i32,) -> i32; -} - -#[cfg(target_arch = "wasm32")] -#[link_section = "component-type:adder"] -#[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 174] = [3, 0, 5, 97, 100, 100, 101, 114, 0, 97, 115, 109, 13, 0, 1, 0, 7, 53, 1, 65, 2, 1, 65, 2, 1, 64, 2, 1, 97, 122, 1, 98, 122, 0, 122, 4, 0, 3, 97, 100, 100, 1, 0, 4, 1, 23, 109, 105, 100, 101, 110, 58, 97, 100, 100, 101, 114, 47, 97, 100, 100, 101, 114, 64, 49, 46, 48, 46, 48, 4, 0, 11, 11, 1, 0, 5, 97, 100, 100, 101, 114, 3, 0, 0, 0, 16, 12, 112, 97, 99, 107, 97, 103, 101, 45, 100, 111, 99, 115, 0, 123, 125, 0, 70, 9, 112, 114, 111, 100, 117, 99, 101, 114, 115, 1, 12, 112, 114, 111, 99, 101, 115, 115, 101, 100, 45, 98, 121, 2, 13, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 6, 48, 46, 49, 56, 46, 50, 16, 119, 105, 116, 45, 98, 105, 110, 100, 103, 101, 110, 45, 114, 117, 115, 116, 6, 48, 46, 49, 54, 46, 48]; - -#[inline(never)] -#[doc(hidden)] -#[cfg(target_arch = "wasm32")] -pub fn __link_section() {} diff --git a/tests/integration/expected/core::cmp::max_u8_u8.hir b/tests/integration/expected/core::cmp::max_u8_u8.hir deleted file mode 100644 index be1e12335..000000000 --- a/tests/integration/expected/core::cmp::max_u8_u8.hir +++ /dev/null @@ -1,21 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = gt v3, v4 : i1; - v6 = cast v5 : i32; - v7 = neq v6, 0 : i1; - v8 = select v7, v0, v1 : i32; - br block1(v8); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/core::cmp::max_u8_u8.wat b/tests/integration/expected/core::cmp::max_u8_u8.wat deleted file mode 100644 index e8d4bc056..000000000 --- a/tests/integration/expected/core::cmp::max_u8_u8.wat +++ /dev/null @@ -1,19 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - local.get 0 - local.get 1 - i32.gt_u - select - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/core::cmp::min_i32_i32.hir b/tests/integration/expected/core::cmp::min_i32_i32.hir deleted file mode 100644 index 56d50999a..000000000 --- a/tests/integration/expected/core::cmp::min_i32_i32.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = lt v0, v1 : i1; - v4 = cast v3 : i32; - v5 = neq v4, 0 : i1; - v6 = select v5, v0, v1 : i32; - ret v6; -} diff --git a/tests/integration/expected/core::cmp::min_i32_i32.wat b/tests/integration/expected/core::cmp::min_i32_i32.wat deleted file mode 100644 index 014acc829..000000000 --- a/tests/integration/expected/core::cmp::min_i32_i32.wat +++ /dev/null @@ -1,19 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - local.get 0 - local.get 1 - i32.lt_s - select - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/core::cmp::min_u32_u32.hir b/tests/integration/expected/core::cmp::min_u32_u32.hir deleted file mode 100644 index 5c6a81cac..000000000 --- a/tests/integration/expected/core::cmp::min_u32_u32.hir +++ /dev/null @@ -1,21 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = lt v3, v4 : i1; - v6 = cast v5 : i32; - v7 = neq v6, 0 : i1; - v8 = select v7, v0, v1 : i32; - br block1(v8); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/core::cmp::min_u32_u32.wat b/tests/integration/expected/core::cmp::min_u32_u32.wat deleted file mode 100644 index 65af6ce9a..000000000 --- a/tests/integration/expected/core::cmp::min_u32_u32.wat +++ /dev/null @@ -1,19 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - local.get 0 - local.get 1 - i32.lt_u - select - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/core::cmp::min_u8_u8.hir b/tests/integration/expected/core::cmp::min_u8_u8.hir deleted file mode 100644 index 5c6a81cac..000000000 --- a/tests/integration/expected/core::cmp::min_u8_u8.hir +++ /dev/null @@ -1,21 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = lt v3, v4 : i1; - v6 = cast v5 : i32; - v7 = neq v6, 0 : i1; - v8 = select v7, v0, v1 : i32; - br block1(v8); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/core::cmp::min_u8_u8.wat b/tests/integration/expected/core::cmp::min_u8_u8.wat deleted file mode 100644 index 65af6ce9a..000000000 --- a/tests/integration/expected/core::cmp::min_u8_u8.wat +++ /dev/null @@ -1,19 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - local.get 0 - local.get 1 - i32.lt_u - select - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/div_u64.hir b/tests/integration/expected/div_u64.hir deleted file mode 100644 index 7575c90b9..000000000 --- a/tests/integration/expected/div_u64.hir +++ /dev/null @@ -1,147 +0,0 @@ -module noname - -const $0 = 0x00100000; -const $1 = 0x001000bc; -const $2 = 0x001000c0; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $1 { id = 1 }; -global external @gv2 : i32 = $2 { id = 2 }; - -pub fn ::type_id(i32, i32) { -block0(v0: i32, v1: i32): - v2 = const.i64 -1688046730280208939 : i64; - v3 = cast v0 : u32; - v4 = add.checked v3, 8 : u32; - v5 = inttoptr v4 : *mut i64; - store v5, v2; - v6 = const.i64 -2518113060735759681 : i64; - v7 = cast v0 : u32; - v8 = inttoptr v7 : *mut i64; - store v8, v6; - ret; -} - -pub fn core::ptr::drop_in_place(i32) { -block0(v0: i32): - ret; -} - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = eq v1, 0 : i1; - v4 = cast v3 : i32; - v5 = neq v4, 0 : i1; - condbr v5, block2, block3; - -block1(v2: i64): - -block2: - v10 = const.i32 1048720 : i32; - v11 = const.i32 25 : i32; - v12 = const.i32 1048692 : i32; - call noname::core::panicking::panic(v10, v11, v12); - unreachable ; - -block3: - v6 = cast v0 : u64; - v7 = cast v1 : u64; - v8 = div.checked v6, v7 : u64; - v9 = cast v8 : i64; - ret v9; -} - -pub fn core::panicking::panic(i32, i32, i32) { -block0(v0: i32, v1: i32, v2: i32): - v3 = const.i32 0 : i32; - v4 = global.load (@__stack_pointer) as *mut i8 : i32; - v5 = const.i32 32 : i32; - v6 = sub.wrapping v4, v5 : i32; - v7 = global.symbol @__stack_pointer : *mut i32; - store v7, v6; - v8 = const.i32 12 : i32; - v9 = add.wrapping v6, v8 : i32; - v10 = const.i64 0 : i64; - v11 = cast v9 : u32; - v12 = inttoptr v11 : *mut i64; - store v12, v10; - v13 = const.i32 1 : i32; - v14 = cast v6 : u32; - v15 = add.checked v14, 4 : u32; - v16 = inttoptr v15 : *mut i32; - store v16, v13; - v17 = const.i32 1048748 : i32; - v18 = cast v6 : u32; - v19 = add.checked v18, 8 : u32; - v20 = inttoptr v19 : *mut i32; - store v20, v17; - v21 = cast v6 : u32; - v22 = add.checked v21, 28 : u32; - v23 = inttoptr v22 : *mut i32; - store v23, v1; - v24 = cast v6 : u32; - v25 = add.checked v24, 24 : u32; - v26 = inttoptr v25 : *mut i32; - store v26, v0; - v27 = const.i32 24 : i32; - v28 = add.wrapping v6, v27 : i32; - v29 = cast v6 : u32; - v30 = inttoptr v29 : *mut i32; - store v30, v28; - call noname::core::panicking::panic_fmt(v6, v2); - unreachable ; - -block1: -} - -pub fn core::panicking::panic_fmt(i32, i32) { -block0(v0: i32, v1: i32): - v2 = const.i32 0 : i32; - v3 = global.load (@__stack_pointer) as *mut i8 : i32; - v4 = const.i32 32 : i32; - v5 = sub.wrapping v3, v4 : i32; - v6 = global.symbol @__stack_pointer : *mut i32; - store v6, v5; - v7 = cast v5 : u32; - v8 = add.checked v7, 24 : u32; - v9 = inttoptr v8 : *mut i32; - store v9, v0; - v10 = const.i32 1048748 : i32; - v11 = cast v5 : u32; - v12 = add.checked v11, 16 : u32; - v13 = inttoptr v12 : *mut i32; - store v13, v10; - v14 = const.i32 1048748 : i32; - v15 = cast v5 : u32; - v16 = add.checked v15, 12 : u32; - v17 = inttoptr v16 : *mut i32; - store v17, v14; - v18 = const.i32 1 : i32; - v19 = trunc v18 : u8; - v20 = cast v5 : u32; - v21 = add.checked v20, 28 : u32; - v22 = inttoptr v21 : *mut u8; - store v22, v19; - v23 = cast v5 : u32; - v24 = add.checked v23, 20 : u32; - v25 = inttoptr v24 : *mut i32; - store v25, v1; - v26 = const.i32 12 : i32; - v27 = add.wrapping v5, v26 : i32; - call noname::rust_begin_unwind(v27); - unreachable ; - -block1: -} - -pub fn rust_begin_unwind(i32) { -block0(v0: i32): - br block2; - -block1: - -block2: - br block2; - -block3: -} diff --git a/tests/integration/expected/div_u64.wat b/tests/integration/expected/div_u64.wat deleted file mode 100644 index 343e90f96..000000000 --- a/tests/integration/expected/div_u64.wat +++ /dev/null @@ -1,109 +0,0 @@ -(module - (type (;0;) (func (param i32))) - (type (;1;) (func (param i64 i64) (result i64))) - (type (;2;) (func (param i32 i32))) - (type (;3;) (func (param i32 i32 i32))) - (func $rust_begin_unwind (;0;) (type 0) (param i32) - loop ;; label = @1 - br 0 (;@1;) - end - ) - (func $entrypoint (;1;) (type 1) (param i64 i64) (result i64) - block ;; label = @1 - local.get 1 - i64.eqz - br_if 0 (;@1;) - local.get 0 - local.get 1 - i64.div_u - return - end - i32.const 1048720 - i32.const 25 - i32.const 1048692 - call $core::panicking::panic - unreachable - ) - (func $core::ptr::drop_in_place (;2;) (type 0) (param i32)) - (func $core::panicking::panic_fmt (;3;) (type 2) (param i32 i32) - (local i32) - global.get $__stack_pointer - i32.const 32 - i32.sub - local.tee 2 - global.set $__stack_pointer - local.get 2 - local.get 0 - i32.store offset=24 - local.get 2 - i32.const 1048748 - i32.store offset=16 - local.get 2 - i32.const 1048748 - i32.store offset=12 - local.get 2 - i32.const 1 - i32.store8 offset=28 - local.get 2 - local.get 1 - i32.store offset=20 - local.get 2 - i32.const 12 - i32.add - call $rust_begin_unwind - unreachable - ) - (func $core::panicking::panic (;4;) (type 3) (param i32 i32 i32) - (local i32) - global.get $__stack_pointer - i32.const 32 - i32.sub - local.tee 3 - global.set $__stack_pointer - local.get 3 - i32.const 12 - i32.add - i64.const 0 - i64.store align=4 - local.get 3 - i32.const 1 - i32.store offset=4 - local.get 3 - i32.const 1048748 - i32.store offset=8 - local.get 3 - local.get 1 - i32.store offset=28 - local.get 3 - local.get 0 - i32.store offset=24 - local.get 3 - local.get 3 - i32.const 24 - i32.add - i32.store - local.get 3 - local.get 2 - call $core::panicking::panic_fmt - unreachable - ) - (func $::type_id (;5;) (type 2) (param i32 i32) - local.get 0 - i64.const -1688046730280208939 - i64.store offset=8 - local.get 0 - i64.const -2518113060735759681 - i64.store - ) - (table (;0;) 3 3 funcref) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048764) - (global (;2;) i32 i32.const 1048768) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) - (elem (;0;) (i32.const 1) func $core::ptr::drop_in_place $::type_id) - (data $.rodata (;0;) (i32.const 1048576) "/var/folders/yg/8x45_9sx5f7b_rk_ldxs49zm0000gp/T/a941a9b84485236e62daed5a82c175eb4c484ea6cd664023ca8feac714c8d6f4.rs\00\00\10\00t\00\00\00\0b\00\00\00C\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00attempt to divide by zero\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\02\00\00\00") -) \ No newline at end of file diff --git a/tests/integration/expected/eq_i16.hir b/tests/integration/expected/eq_i16.hir deleted file mode 100644 index a4feb9b29..000000000 --- a/tests/integration/expected/eq_i16.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = eq v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/eq_i16.masm b/tests/integration/expected/eq_i16.masm deleted file mode 100644 index 2f2f1859a..000000000 --- a/tests/integration/expected/eq_i16.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - eq -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/eq_i16.wat b/tests/integration/expected/eq_i16.wat deleted file mode 100644 index 2b6b25055..000000000 --- a/tests/integration/expected/eq_i16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.eq - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/eq_i32.hir b/tests/integration/expected/eq_i32.hir deleted file mode 100644 index a4feb9b29..000000000 --- a/tests/integration/expected/eq_i32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = eq v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/eq_i32.masm b/tests/integration/expected/eq_i32.masm deleted file mode 100644 index 2f2f1859a..000000000 --- a/tests/integration/expected/eq_i32.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - eq -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/eq_i32.wat b/tests/integration/expected/eq_i32.wat deleted file mode 100644 index 2b6b25055..000000000 --- a/tests/integration/expected/eq_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.eq - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/eq_i64.hir b/tests/integration/expected/eq_i64.hir deleted file mode 100644 index f45d72a9e..000000000 --- a/tests/integration/expected/eq_i64.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i32 { -block0(v0: i64, v1: i64): - v3 = eq v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/eq_i64.masm b/tests/integration/expected/eq_i64.masm deleted file mode 100644 index 0fb2ce1b8..000000000 --- a/tests/integration/expected/eq_i64.masm +++ /dev/null @@ -1,674 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - movdn.3 - movdn.3 - movup.2 - eq - movdn.3 - eq - and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/eq_i64.wat b/tests/integration/expected/eq_i64.wat deleted file mode 100644 index f22c85381..000000000 --- a/tests/integration/expected/eq_i64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i32))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i32) - local.get 0 - local.get 1 - i64.eq - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/eq_i8.hir b/tests/integration/expected/eq_i8.hir deleted file mode 100644 index a4feb9b29..000000000 --- a/tests/integration/expected/eq_i8.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = eq v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/eq_i8.masm b/tests/integration/expected/eq_i8.masm deleted file mode 100644 index 2f2f1859a..000000000 --- a/tests/integration/expected/eq_i8.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - eq -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/eq_i8.wat b/tests/integration/expected/eq_i8.wat deleted file mode 100644 index 2b6b25055..000000000 --- a/tests/integration/expected/eq_i8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.eq - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/eq_u16.hir b/tests/integration/expected/eq_u16.hir deleted file mode 100644 index a4feb9b29..000000000 --- a/tests/integration/expected/eq_u16.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = eq v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/eq_u16.masm b/tests/integration/expected/eq_u16.masm deleted file mode 100644 index 2f2f1859a..000000000 --- a/tests/integration/expected/eq_u16.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - eq -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/eq_u16.wat b/tests/integration/expected/eq_u16.wat deleted file mode 100644 index 2b6b25055..000000000 --- a/tests/integration/expected/eq_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.eq - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/eq_u32.hir b/tests/integration/expected/eq_u32.hir deleted file mode 100644 index a4feb9b29..000000000 --- a/tests/integration/expected/eq_u32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = eq v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/eq_u32.masm b/tests/integration/expected/eq_u32.masm deleted file mode 100644 index 2f2f1859a..000000000 --- a/tests/integration/expected/eq_u32.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - eq -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/eq_u32.wat b/tests/integration/expected/eq_u32.wat deleted file mode 100644 index 2b6b25055..000000000 --- a/tests/integration/expected/eq_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.eq - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/eq_u64.hir b/tests/integration/expected/eq_u64.hir deleted file mode 100644 index f45d72a9e..000000000 --- a/tests/integration/expected/eq_u64.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i32 { -block0(v0: i64, v1: i64): - v3 = eq v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/eq_u64.masm b/tests/integration/expected/eq_u64.masm deleted file mode 100644 index 0fb2ce1b8..000000000 --- a/tests/integration/expected/eq_u64.masm +++ /dev/null @@ -1,674 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - movdn.3 - movdn.3 - movup.2 - eq - movdn.3 - eq - and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/eq_u64.wat b/tests/integration/expected/eq_u64.wat deleted file mode 100644 index f22c85381..000000000 --- a/tests/integration/expected/eq_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i32))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i32) - local.get 0 - local.get 1 - i64.eq - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/eq_u8.hir b/tests/integration/expected/eq_u8.hir deleted file mode 100644 index a4feb9b29..000000000 --- a/tests/integration/expected/eq_u8.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = eq v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/eq_u8.masm b/tests/integration/expected/eq_u8.masm deleted file mode 100644 index 2f2f1859a..000000000 --- a/tests/integration/expected/eq_u8.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - eq -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/eq_u8.wat b/tests/integration/expected/eq_u8.wat deleted file mode 100644 index 2b6b25055..000000000 --- a/tests/integration/expected/eq_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.eq - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/fib.hir b/tests/integration/expected/fib.hir deleted file mode 100644 index 5d2d588c0..000000000 --- a/tests/integration/expected/fib.hir +++ /dev/null @@ -1,32 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn fib(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 0 : i32; - v3 = const.i32 0 : i32; - v4 = const.i32 1 : i32; - br block2(v4, v0, v3); - -block1(v1: i32): - -block2(v6: i32, v7: i32, v9: i32): - v8 = neq v7, 0 : i1; - condbr v8, block4, block5; - -block3(v5: i32): - -block4: - v10 = const.i32 -1 : i32; - v11 = add.wrapping v7, v10 : i32; - v12 = add.wrapping v9, v6 : i32; - br block2(v12, v11, v6); - -block5: - ret v9; -} diff --git a/tests/integration/expected/fib.masm b/tests/integration/expected/fib.masm deleted file mode 100644 index 77378bb31..000000000 --- a/tests/integration/expected/fib.masm +++ /dev/null @@ -1,696 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.fib - push.0 - push.1 - movup.2 - swap.1 - dup.1 - neq.0 - push.1 - while.true - if.true - push.4294967295 - movup.2 - swap.1 - u32wrapping_add - dup.1 - swap.1 - swap.3 - swap.1 - u32wrapping_add - movup.2 - swap.1 - dup.1 - neq.0 - push.1 - else - drop - drop - push.0 - end - end -end - -program - -use noname - -begin - exec.noname::fib -end diff --git a/tests/integration/expected/fib.wat b/tests/integration/expected/fib.wat deleted file mode 100644 index 11827bcfb..000000000 --- a/tests/integration/expected/fib.wat +++ /dev/null @@ -1,40 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $fib (;0;) (type 0) (param i32) (result i32) - (local i32 i32 i32) - i32.const 0 - local.set 1 - i32.const 1 - local.set 2 - loop (result i32) ;; label = @1 - local.get 2 - local.set 3 - block ;; label = @2 - local.get 0 - br_if 0 (;@2;) - local.get 1 - return - end - local.get 0 - i32.const -1 - i32.add - local.set 0 - local.get 1 - local.get 3 - i32.add - local.set 2 - local.get 3 - local.set 1 - 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) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "fib" (func $fib)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/ge_i32.hir b/tests/integration/expected/ge_i32.hir deleted file mode 100644 index 36858c4dd..000000000 --- a/tests/integration/expected/ge_i32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = gte v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/ge_i32.masm b/tests/integration/expected/ge_i32.masm deleted file mode 100644 index 63c1764ad..000000000 --- a/tests/integration/expected/ge_i32.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - swap.1 - exec.is_gte -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/ge_i32.wat b/tests/integration/expected/ge_i32.wat deleted file mode 100644 index e0bbbf635..000000000 --- a/tests/integration/expected/ge_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.ge_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/ge_u16.hir b/tests/integration/expected/ge_u16.hir deleted file mode 100644 index 410a5624c..000000000 --- a/tests/integration/expected/ge_u16.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = gte v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/ge_u16.masm b/tests/integration/expected/ge_u16.masm deleted file mode 100644 index 3512a445d..000000000 --- a/tests/integration/expected/ge_u16.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32gte -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/ge_u16.wat b/tests/integration/expected/ge_u16.wat deleted file mode 100644 index 859b578ef..000000000 --- a/tests/integration/expected/ge_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.ge_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/ge_u32.hir b/tests/integration/expected/ge_u32.hir deleted file mode 100644 index 410a5624c..000000000 --- a/tests/integration/expected/ge_u32.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = gte v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/ge_u32.masm b/tests/integration/expected/ge_u32.masm deleted file mode 100644 index 3512a445d..000000000 --- a/tests/integration/expected/ge_u32.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32gte -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/ge_u32.wat b/tests/integration/expected/ge_u32.wat deleted file mode 100644 index 859b578ef..000000000 --- a/tests/integration/expected/ge_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.ge_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/ge_u64.hir b/tests/integration/expected/ge_u64.hir deleted file mode 100644 index 8b2181bac..000000000 --- a/tests/integration/expected/ge_u64.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i32 { -block0(v0: i64, v1: i64): - v3 = cast v0 : u64; - v4 = cast v1 : u64; - v5 = gte v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/ge_u64.wat b/tests/integration/expected/ge_u64.wat deleted file mode 100644 index 7d8760d27..000000000 --- a/tests/integration/expected/ge_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i32))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i32) - local.get 0 - local.get 1 - i64.ge_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/ge_u8.hir b/tests/integration/expected/ge_u8.hir deleted file mode 100644 index 410a5624c..000000000 --- a/tests/integration/expected/ge_u8.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = gte v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/ge_u8.masm b/tests/integration/expected/ge_u8.masm deleted file mode 100644 index 3512a445d..000000000 --- a/tests/integration/expected/ge_u8.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32gte -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/ge_u8.wat b/tests/integration/expected/ge_u8.wat deleted file mode 100644 index 859b578ef..000000000 --- a/tests/integration/expected/ge_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.ge_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/gt_i32.hir b/tests/integration/expected/gt_i32.hir deleted file mode 100644 index 801346862..000000000 --- a/tests/integration/expected/gt_i32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = gt v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/gt_i32.masm b/tests/integration/expected/gt_i32.masm deleted file mode 100644 index 10aabef61..000000000 --- a/tests/integration/expected/gt_i32.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - swap.1 - exec.is_gt -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/gt_i32.wat b/tests/integration/expected/gt_i32.wat deleted file mode 100644 index 4eb94daad..000000000 --- a/tests/integration/expected/gt_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.gt_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/gt_u16.hir b/tests/integration/expected/gt_u16.hir deleted file mode 100644 index 0da10188c..000000000 --- a/tests/integration/expected/gt_u16.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = gt v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/gt_u16.masm b/tests/integration/expected/gt_u16.masm deleted file mode 100644 index 418e06bc6..000000000 --- a/tests/integration/expected/gt_u16.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32gt -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/gt_u16.wat b/tests/integration/expected/gt_u16.wat deleted file mode 100644 index d7fb51e46..000000000 --- a/tests/integration/expected/gt_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.gt_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/gt_u32.hir b/tests/integration/expected/gt_u32.hir deleted file mode 100644 index 0da10188c..000000000 --- a/tests/integration/expected/gt_u32.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = gt v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/gt_u32.masm b/tests/integration/expected/gt_u32.masm deleted file mode 100644 index 418e06bc6..000000000 --- a/tests/integration/expected/gt_u32.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32gt -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/gt_u32.wat b/tests/integration/expected/gt_u32.wat deleted file mode 100644 index d7fb51e46..000000000 --- a/tests/integration/expected/gt_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.gt_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/gt_u64.hir b/tests/integration/expected/gt_u64.hir deleted file mode 100644 index 6df01815e..000000000 --- a/tests/integration/expected/gt_u64.hir +++ /dev/null @@ -1,19 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i32 { -block0(v0: i64, v1: i64): - v3 = cast v0 : u64; - v4 = cast v1 : u64; - v5 = gt v3, v4 : i1; - v6 = cast v5 : i32; - br block1(v6); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/gt_u64.wat b/tests/integration/expected/gt_u64.wat deleted file mode 100644 index 58d1e7211..000000000 --- a/tests/integration/expected/gt_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i32))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i32) - local.get 0 - local.get 1 - i64.gt_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/gt_u8.hir b/tests/integration/expected/gt_u8.hir deleted file mode 100644 index 0da10188c..000000000 --- a/tests/integration/expected/gt_u8.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = gt v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/gt_u8.masm b/tests/integration/expected/gt_u8.masm deleted file mode 100644 index 418e06bc6..000000000 --- a/tests/integration/expected/gt_u8.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32gt -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/gt_u8.wat b/tests/integration/expected/gt_u8.wat deleted file mode 100644 index d7fb51e46..000000000 --- a/tests/integration/expected/gt_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.gt_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/le_i32.hir b/tests/integration/expected/le_i32.hir deleted file mode 100644 index fec85e11f..000000000 --- a/tests/integration/expected/le_i32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = lte v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/le_i32.masm b/tests/integration/expected/le_i32.masm deleted file mode 100644 index b81087057..000000000 --- a/tests/integration/expected/le_i32.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - swap.1 - exec.is_lte -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/le_i32.wat b/tests/integration/expected/le_i32.wat deleted file mode 100644 index 8b62129bb..000000000 --- a/tests/integration/expected/le_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.le_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/le_u16.hir b/tests/integration/expected/le_u16.hir deleted file mode 100644 index a2572b375..000000000 --- a/tests/integration/expected/le_u16.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = lte v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/le_u16.masm b/tests/integration/expected/le_u16.masm deleted file mode 100644 index ee4ab1741..000000000 --- a/tests/integration/expected/le_u16.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32lte -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/le_u16.wat b/tests/integration/expected/le_u16.wat deleted file mode 100644 index 06218d9fc..000000000 --- a/tests/integration/expected/le_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.le_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/le_u32.hir b/tests/integration/expected/le_u32.hir deleted file mode 100644 index a2572b375..000000000 --- a/tests/integration/expected/le_u32.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = lte v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/le_u32.masm b/tests/integration/expected/le_u32.masm deleted file mode 100644 index ee4ab1741..000000000 --- a/tests/integration/expected/le_u32.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32lte -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/le_u32.wat b/tests/integration/expected/le_u32.wat deleted file mode 100644 index 06218d9fc..000000000 --- a/tests/integration/expected/le_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.le_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/le_u64.hir b/tests/integration/expected/le_u64.hir deleted file mode 100644 index 776f467a1..000000000 --- a/tests/integration/expected/le_u64.hir +++ /dev/null @@ -1,19 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i32 { -block0(v0: i64, v1: i64): - v3 = cast v0 : u64; - v4 = cast v1 : u64; - v5 = lte v3, v4 : i1; - v6 = cast v5 : i32; - br block1(v6); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/le_u64.wat b/tests/integration/expected/le_u64.wat deleted file mode 100644 index 2eb7fb660..000000000 --- a/tests/integration/expected/le_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i32))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i32) - local.get 0 - local.get 1 - i64.le_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/le_u8.hir b/tests/integration/expected/le_u8.hir deleted file mode 100644 index a2572b375..000000000 --- a/tests/integration/expected/le_u8.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = lte v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/le_u8.masm b/tests/integration/expected/le_u8.masm deleted file mode 100644 index ee4ab1741..000000000 --- a/tests/integration/expected/le_u8.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32lte -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/le_u8.wat b/tests/integration/expected/le_u8.wat deleted file mode 100644 index 06218d9fc..000000000 --- a/tests/integration/expected/le_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.le_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/lt_i32.hir b/tests/integration/expected/lt_i32.hir deleted file mode 100644 index 371b88c20..000000000 --- a/tests/integration/expected/lt_i32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = lt v0, v1 : i1; - v4 = cast v3 : i32; - ret v4; -} diff --git a/tests/integration/expected/lt_i32.masm b/tests/integration/expected/lt_i32.masm deleted file mode 100644 index 1915b8667..000000000 --- a/tests/integration/expected/lt_i32.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - swap.1 - exec.is_lt -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/lt_i32.wat b/tests/integration/expected/lt_i32.wat deleted file mode 100644 index 05cd39198..000000000 --- a/tests/integration/expected/lt_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.lt_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/lt_i8.hir b/tests/integration/expected/lt_i8.hir deleted file mode 100644 index d0b0a9305..000000000 --- a/tests/integration/expected/lt_i8.hir +++ /dev/null @@ -1,17 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = lt v0, v1 : i1; - v4 = cast v3 : i32; - br block1(v4); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/lt_i8.wat b/tests/integration/expected/lt_i8.wat deleted file mode 100644 index 05cd39198..000000000 --- a/tests/integration/expected/lt_i8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.lt_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/lt_u16.hir b/tests/integration/expected/lt_u16.hir deleted file mode 100644 index 7759d7a78..000000000 --- a/tests/integration/expected/lt_u16.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = lt v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/lt_u16.masm b/tests/integration/expected/lt_u16.masm deleted file mode 100644 index 6bb50b9ec..000000000 --- a/tests/integration/expected/lt_u16.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32lt -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/lt_u16.wat b/tests/integration/expected/lt_u16.wat deleted file mode 100644 index 663d15e42..000000000 --- a/tests/integration/expected/lt_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.lt_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/lt_u32.hir b/tests/integration/expected/lt_u32.hir deleted file mode 100644 index 7759d7a78..000000000 --- a/tests/integration/expected/lt_u32.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = lt v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/lt_u32.masm b/tests/integration/expected/lt_u32.masm deleted file mode 100644 index 6bb50b9ec..000000000 --- a/tests/integration/expected/lt_u32.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32lt -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/lt_u32.wat b/tests/integration/expected/lt_u32.wat deleted file mode 100644 index 663d15e42..000000000 --- a/tests/integration/expected/lt_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.lt_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/lt_u64.hir b/tests/integration/expected/lt_u64.hir deleted file mode 100644 index a4fd19ed1..000000000 --- a/tests/integration/expected/lt_u64.hir +++ /dev/null @@ -1,19 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i32 { -block0(v0: i64, v1: i64): - v3 = cast v0 : u64; - v4 = cast v1 : u64; - v5 = lt v3, v4 : i1; - v6 = cast v5 : i32; - br block1(v6); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/lt_u64.wat b/tests/integration/expected/lt_u64.wat deleted file mode 100644 index a00d37354..000000000 --- a/tests/integration/expected/lt_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i32))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i32) - local.get 0 - local.get 1 - i64.lt_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/lt_u8.hir b/tests/integration/expected/lt_u8.hir deleted file mode 100644 index 7759d7a78..000000000 --- a/tests/integration/expected/lt_u8.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = lt v3, v4 : i1; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/lt_u8.masm b/tests/integration/expected/lt_u8.masm deleted file mode 100644 index 6bb50b9ec..000000000 --- a/tests/integration/expected/lt_u8.masm +++ /dev/null @@ -1,679 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32lt -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/lt_u8.wat b/tests/integration/expected/lt_u8.wat deleted file mode 100644 index 663d15e42..000000000 --- a/tests/integration/expected/lt_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.lt_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/mul_i64.hir b/tests/integration/expected/mul_i64.hir deleted file mode 100644 index 2db19395c..000000000 --- a/tests/integration/expected/mul_i64.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = mul.wrapping v1, v0 : i64; - ret v3; -} diff --git a/tests/integration/expected/mul_i64.wat b/tests/integration/expected/mul_i64.wat deleted file mode 100644 index a4349bbc0..000000000 --- a/tests/integration/expected/mul_i64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 1 - local.get 0 - i64.mul - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/mul_u32.hir b/tests/integration/expected/mul_u32.hir deleted file mode 100644 index 0c65e6d8b..000000000 --- a/tests/integration/expected/mul_u32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = mul.wrapping v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/mul_u32.masm b/tests/integration/expected/mul_u32.masm deleted file mode 100644 index 2d29266ab..000000000 --- a/tests/integration/expected/mul_u32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - exec.wrapping_mul -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/mul_u32.wat b/tests/integration/expected/mul_u32.wat deleted file mode 100644 index 0c98731fa..000000000 --- a/tests/integration/expected/mul_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.mul - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/mul_u64.hir b/tests/integration/expected/mul_u64.hir deleted file mode 100644 index 2db19395c..000000000 --- a/tests/integration/expected/mul_u64.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = mul.wrapping v1, v0 : i64; - ret v3; -} diff --git a/tests/integration/expected/mul_u64.wat b/tests/integration/expected/mul_u64.wat deleted file mode 100644 index a4349bbc0..000000000 --- a/tests/integration/expected/mul_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 1 - local.get 0 - i64.mul - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/neg_i16.hir b/tests/integration/expected/neg_i16.hir deleted file mode 100644 index d5701cc7e..000000000 --- a/tests/integration/expected/neg_i16.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 0 : i32; - v3 = sub.wrapping v2, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/neg_i16.masm b/tests/integration/expected/neg_i16.masm deleted file mode 100644 index c1a2a1833..000000000 --- a/tests/integration/expected/neg_i16.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.0 - swap.1 - u32wrapping_sub -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/neg_i16.wat b/tests/integration/expected/neg_i16.wat deleted file mode 100644 index c606efa95..000000000 --- a/tests/integration/expected/neg_i16.wat +++ /dev/null @@ -1,17 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - i32.const 0 - local.get 0 - i32.sub - i32.extend16_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/neg_i32.hir b/tests/integration/expected/neg_i32.hir deleted file mode 100644 index d5701cc7e..000000000 --- a/tests/integration/expected/neg_i32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 0 : i32; - v3 = sub.wrapping v2, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/neg_i32.masm b/tests/integration/expected/neg_i32.masm deleted file mode 100644 index c1a2a1833..000000000 --- a/tests/integration/expected/neg_i32.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.0 - swap.1 - u32wrapping_sub -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/neg_i32.wat b/tests/integration/expected/neg_i32.wat deleted file mode 100644 index 716a484c2..000000000 --- a/tests/integration/expected/neg_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - i32.const 0 - local.get 0 - i32.sub - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/neg_i64.hir b/tests/integration/expected/neg_i64.hir deleted file mode 100644 index b9c385fd3..000000000 --- a/tests/integration/expected/neg_i64.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64) -> i64 { -block0(v0: i64): - v2 = const.i64 0 : i64; - v3 = sub.wrapping v2, v0 : i64; - ret v3; -} diff --git a/tests/integration/expected/neg_i64.wat b/tests/integration/expected/neg_i64.wat deleted file mode 100644 index fc8e1f75a..000000000 --- a/tests/integration/expected/neg_i64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64) (result i64) - i64.const 0 - local.get 0 - i64.sub - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/neg_i8.hir b/tests/integration/expected/neg_i8.hir deleted file mode 100644 index d5701cc7e..000000000 --- a/tests/integration/expected/neg_i8.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 0 : i32; - v3 = sub.wrapping v2, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/neg_i8.masm b/tests/integration/expected/neg_i8.masm deleted file mode 100644 index c1a2a1833..000000000 --- a/tests/integration/expected/neg_i8.masm +++ /dev/null @@ -1,670 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.0 - swap.1 - u32wrapping_sub -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/neg_i8.wat b/tests/integration/expected/neg_i8.wat deleted file mode 100644 index 453dc1490..000000000 --- a/tests/integration/expected/neg_i8.wat +++ /dev/null @@ -1,17 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - i32.const 0 - local.get 0 - i32.sub - i32.extend8_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_bool.hir b/tests/integration/expected/not_bool.hir deleted file mode 100644 index 2ac9fc746..000000000 --- a/tests/integration/expected/not_bool.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 1 : i32; - v3 = bxor v0, v2 : i32; - ret v3; -} diff --git a/tests/integration/expected/not_bool.masm b/tests/integration/expected/not_bool.masm deleted file mode 100644 index 9728b1edf..000000000 --- a/tests/integration/expected/not_bool.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.1 - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/not_bool.wat b/tests/integration/expected/not_bool.wat deleted file mode 100644 index 055fd8066..000000000 --- a/tests/integration/expected/not_bool.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - local.get 0 - i32.const 1 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_i16.hir b/tests/integration/expected/not_i16.hir deleted file mode 100644 index 986470bfe..000000000 --- a/tests/integration/expected/not_i16.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 -1 : i32; - v3 = bxor v0, v2 : i32; - ret v3; -} diff --git a/tests/integration/expected/not_i16.masm b/tests/integration/expected/not_i16.masm deleted file mode 100644 index 14672b8ca..000000000 --- a/tests/integration/expected/not_i16.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.4294967295 - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/not_i16.wat b/tests/integration/expected/not_i16.wat deleted file mode 100644 index eb6ccfe8e..000000000 --- a/tests/integration/expected/not_i16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - local.get 0 - i32.const -1 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_i32.hir b/tests/integration/expected/not_i32.hir deleted file mode 100644 index 986470bfe..000000000 --- a/tests/integration/expected/not_i32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 -1 : i32; - v3 = bxor v0, v2 : i32; - ret v3; -} diff --git a/tests/integration/expected/not_i32.masm b/tests/integration/expected/not_i32.masm deleted file mode 100644 index 14672b8ca..000000000 --- a/tests/integration/expected/not_i32.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.4294967295 - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/not_i32.wat b/tests/integration/expected/not_i32.wat deleted file mode 100644 index eb6ccfe8e..000000000 --- a/tests/integration/expected/not_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - local.get 0 - i32.const -1 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_i64.hir b/tests/integration/expected/not_i64.hir deleted file mode 100644 index d4923cdf8..000000000 --- a/tests/integration/expected/not_i64.hir +++ /dev/null @@ -1,17 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64) -> i64 { -block0(v0: i64): - v2 = const.i64 -1 : i64; - v3 = bxor v0, v2 : i64; - br block1(v3); - -block1(v1: i64): - ret v1; -} diff --git a/tests/integration/expected/not_i64.masm b/tests/integration/expected/not_i64.masm deleted file mode 100644 index de32bb477..000000000 --- a/tests/integration/expected/not_i64.masm +++ /dev/null @@ -1,9 +0,0 @@ -export.entrypoint - push.4294967295.4294967295 - exec.checked_xor - -end -begin - exec.entrypoint -end - diff --git a/tests/integration/expected/not_i64.wat b/tests/integration/expected/not_i64.wat deleted file mode 100644 index 0b8c1249d..000000000 --- a/tests/integration/expected/not_i64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64) (result i64) - local.get 0 - i64.const -1 - i64.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_i8.hir b/tests/integration/expected/not_i8.hir deleted file mode 100644 index 986470bfe..000000000 --- a/tests/integration/expected/not_i8.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 -1 : i32; - v3 = bxor v0, v2 : i32; - ret v3; -} diff --git a/tests/integration/expected/not_i8.masm b/tests/integration/expected/not_i8.masm deleted file mode 100644 index 14672b8ca..000000000 --- a/tests/integration/expected/not_i8.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.4294967295 - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/not_i8.wat b/tests/integration/expected/not_i8.wat deleted file mode 100644 index eb6ccfe8e..000000000 --- a/tests/integration/expected/not_i8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - local.get 0 - i32.const -1 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_u16.hir b/tests/integration/expected/not_u16.hir deleted file mode 100644 index 448366f74..000000000 --- a/tests/integration/expected/not_u16.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 65535 : i32; - v3 = bxor v0, v2 : i32; - ret v3; -} diff --git a/tests/integration/expected/not_u16.masm b/tests/integration/expected/not_u16.masm deleted file mode 100644 index c9a5cbecf..000000000 --- a/tests/integration/expected/not_u16.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.65535 - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/not_u16.wat b/tests/integration/expected/not_u16.wat deleted file mode 100644 index 32b06b522..000000000 --- a/tests/integration/expected/not_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - local.get 0 - i32.const 65535 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_u32.hir b/tests/integration/expected/not_u32.hir deleted file mode 100644 index 986470bfe..000000000 --- a/tests/integration/expected/not_u32.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 -1 : i32; - v3 = bxor v0, v2 : i32; - ret v3; -} diff --git a/tests/integration/expected/not_u32.masm b/tests/integration/expected/not_u32.masm deleted file mode 100644 index 14672b8ca..000000000 --- a/tests/integration/expected/not_u32.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.4294967295 - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/not_u32.wat b/tests/integration/expected/not_u32.wat deleted file mode 100644 index eb6ccfe8e..000000000 --- a/tests/integration/expected/not_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - local.get 0 - i32.const -1 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_u64.hir b/tests/integration/expected/not_u64.hir deleted file mode 100644 index 3b5c71bae..000000000 --- a/tests/integration/expected/not_u64.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64) -> i64 { -block0(v0: i64): - v2 = const.i64 -1 : i64; - v3 = bxor v0, v2 : i64; - ret v3; -} diff --git a/tests/integration/expected/not_u64.masm b/tests/integration/expected/not_u64.masm deleted file mode 100644 index 0a5c0acb8..000000000 --- a/tests/integration/expected/not_u64.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.4294967295.4294967295 - exec.checked_xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/not_u64.wat b/tests/integration/expected/not_u64.wat deleted file mode 100644 index 0b8c1249d..000000000 --- a/tests/integration/expected/not_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64) (result i64) - local.get 0 - i64.const -1 - i64.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/not_u8.hir b/tests/integration/expected/not_u8.hir deleted file mode 100644 index 216b9bb2e..000000000 --- a/tests/integration/expected/not_u8.hir +++ /dev/null @@ -1,14 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32) -> i32 { -block0(v0: i32): - v2 = const.i32 255 : i32; - v3 = bxor v0, v2 : i32; - ret v3; -} diff --git a/tests/integration/expected/not_u8.masm b/tests/integration/expected/not_u8.masm deleted file mode 100644 index 816184375..000000000 --- a/tests/integration/expected/not_u8.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.255 - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/not_u8.wat b/tests/integration/expected/not_u8.wat deleted file mode 100644 index 8e0f8cc7e..000000000 --- a/tests/integration/expected/not_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32) (result i32) - local.get 0 - i32.const 255 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/or_bool.hir b/tests/integration/expected/or_bool.hir deleted file mode 100644 index 9f538b178..000000000 --- a/tests/integration/expected/or_bool.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bor v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/or_bool.masm b/tests/integration/expected/or_bool.masm deleted file mode 100644 index 5e6d5aee9..000000000 --- a/tests/integration/expected/or_bool.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32or -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/or_bool.wat b/tests/integration/expected/or_bool.wat deleted file mode 100644 index 6125d5b6e..000000000 --- a/tests/integration/expected/or_bool.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.or - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/or_i16.hir b/tests/integration/expected/or_i16.hir deleted file mode 100644 index b419e3a57..000000000 --- a/tests/integration/expected/or_i16.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/or_i16.masm b/tests/integration/expected/or_i16.masm deleted file mode 100644 index aeec12d96..000000000 --- a/tests/integration/expected/or_i16.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32or -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/or_i16.wat b/tests/integration/expected/or_i16.wat deleted file mode 100644 index 78dd8119a..000000000 --- a/tests/integration/expected/or_i16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.or - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/or_i32.hir b/tests/integration/expected/or_i32.hir deleted file mode 100644 index b419e3a57..000000000 --- a/tests/integration/expected/or_i32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/or_i32.masm b/tests/integration/expected/or_i32.masm deleted file mode 100644 index aeec12d96..000000000 --- a/tests/integration/expected/or_i32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32or -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/or_i32.wat b/tests/integration/expected/or_i32.wat deleted file mode 100644 index 78dd8119a..000000000 --- a/tests/integration/expected/or_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.or - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/or_i8.hir b/tests/integration/expected/or_i8.hir deleted file mode 100644 index b419e3a57..000000000 --- a/tests/integration/expected/or_i8.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/or_i8.masm b/tests/integration/expected/or_i8.masm deleted file mode 100644 index aeec12d96..000000000 --- a/tests/integration/expected/or_i8.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32or -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/or_i8.wat b/tests/integration/expected/or_i8.wat deleted file mode 100644 index 78dd8119a..000000000 --- a/tests/integration/expected/or_i8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.or - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/or_u16.hir b/tests/integration/expected/or_u16.hir deleted file mode 100644 index b419e3a57..000000000 --- a/tests/integration/expected/or_u16.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/or_u16.masm b/tests/integration/expected/or_u16.masm deleted file mode 100644 index aeec12d96..000000000 --- a/tests/integration/expected/or_u16.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32or -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/or_u16.wat b/tests/integration/expected/or_u16.wat deleted file mode 100644 index 78dd8119a..000000000 --- a/tests/integration/expected/or_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.or - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/or_u32.hir b/tests/integration/expected/or_u32.hir deleted file mode 100644 index b419e3a57..000000000 --- a/tests/integration/expected/or_u32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/or_u32.masm b/tests/integration/expected/or_u32.masm deleted file mode 100644 index aeec12d96..000000000 --- a/tests/integration/expected/or_u32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32or -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/or_u32.wat b/tests/integration/expected/or_u32.wat deleted file mode 100644 index 78dd8119a..000000000 --- a/tests/integration/expected/or_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.or - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/or_u64.hir b/tests/integration/expected/or_u64.hir deleted file mode 100644 index 739eb6ae3..000000000 --- a/tests/integration/expected/or_u64.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = bor v1, v0 : i64; - ret v3; -} diff --git a/tests/integration/expected/or_u64.masm b/tests/integration/expected/or_u64.masm deleted file mode 100644 index b77af770d..000000000 --- a/tests/integration/expected/or_u64.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - exec.checked_or -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/or_u64.wat b/tests/integration/expected/or_u64.wat deleted file mode 100644 index a75364c6e..000000000 --- a/tests/integration/expected/or_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 1 - local.get 0 - i64.or - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/or_u8.hir b/tests/integration/expected/or_u8.hir deleted file mode 100644 index b419e3a57..000000000 --- a/tests/integration/expected/or_u8.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/or_u8.masm b/tests/integration/expected/or_u8.masm deleted file mode 100644 index aeec12d96..000000000 --- a/tests/integration/expected/or_u8.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32or -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/or_u8.wat b/tests/integration/expected/or_u8.wat deleted file mode 100644 index 78dd8119a..000000000 --- a/tests/integration/expected/or_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.or - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/sdk_basic_wallet/basic_wallet.wat b/tests/integration/expected/sdk_basic_wallet/basic_wallet.wat deleted file mode 100644 index e0e36101d..000000000 --- a/tests/integration/expected/sdk_basic_wallet/basic_wallet.wat +++ /dev/null @@ -1,6397 +0,0 @@ -(component - (type (;0;) - (instance - (type (;0;) u64) - (export (;1;) "felt" (type (eq 0))) - (export (;2;) "account-id" (type (eq 1))) - (type (;3;) (record (field "asset" 2) (field "amount" u64))) - (export (;4;) "fungible-asset" (type (eq 3))) - (type (;5;) (tuple 1 1 1 1)) - (export (;6;) "word" (type (eq 5))) - (export (;7;) "non-fungible-asset" (type (eq 6))) - (type (;8;) (variant (case "fungible" 4) (case "non-fungible" 7))) - (export (;9;) "asset" (type (eq 8))) - (export (;10;) "tag" (type (eq 1))) - (export (;11;) "recipient" (type (eq 6))) - ) - ) - (import "miden:base/types@1.0.0" (instance (;0;) (type 0))) - (alias export 0 "asset" (type (;1;))) - (alias export 0 "tag" (type (;2;))) - (alias export 0 "recipient" (type (;3;))) - (type (;4;) - (instance - (alias outer 1 1 (type (;0;))) - (export (;1;) "asset" (type (eq 0))) - (alias outer 1 2 (type (;2;))) - (export (;3;) "tag" (type (eq 2))) - (alias outer 1 3 (type (;4;))) - (export (;5;) "recipient" (type (eq 4))) - (type (;6;) (func (param "asset" 1) (result 1))) - (export (;0;) "add-asset" (func (type 6))) - (export (;1;) "remove-asset" (func (type 6))) - (type (;7;) (func (param "asset" 1) (param "tag" 3) (param "recipient" 5))) - (export (;2;) "create-note" (func (type 7))) - ) - ) - (import "miden:base/tx-kernel@1.0.0" (instance (;1;) (type 4))) - (core module (;0;) - (type (;0;) (func (param i32 i64 i64 i64 i64 i32))) - (type (;1;) (func (param i32 i64 i64 i64 i64 i64 i64 i64 i64 i64))) - (type (;2;) (func)) - (type (;3;) (func (param i32 i64 i64 i64 i64))) - (type (;4;) (func (param i32 i32) (result i32))) - (type (;5;) (func (param i32 i32 i32 i32) (result i32))) - (type (;6;) (func (param i32) (result i32))) - (type (;7;) (func (param i32))) - (type (;8;) (func (param i32 i32))) - (type (;9;) (func (param i32 i32 i32) (result i32))) - (import "miden:base/tx-kernel@1.0.0" "add-asset" (func $basic_wallet::bindings::miden::base::tx_kernel::add_asset::wit_import (;0;) (type 0))) - (import "miden:base/tx-kernel@1.0.0" "remove-asset" (func $basic_wallet::bindings::miden::base::tx_kernel::remove_asset::wit_import (;1;) (type 0))) - (import "miden:base/tx-kernel@1.0.0" "create-note" (func $basic_wallet::bindings::miden::base::tx_kernel::create_note::wit_import (;2;) (type 1))) - (func $__wasm_call_ctors (;3;) (type 2)) - (func $miden:basic-wallet/basic-wallet@1.0.0#receive-asset (;4;) (type 3) (param i32 i64 i64 i64 i64) - (local i32) - global.get $__stack_pointer - i32.const 48 - i32.sub - local.tee 5 - global.set $__stack_pointer - call $wit_bindgen::rt::run_ctors_once - local.get 0 - i32.const 0 - i32.ne - local.get 1 - local.get 2 - local.get 3 - i64.const 0 - local.get 0 - select - local.get 4 - i64.const 0 - local.get 0 - select - local.get 5 - i32.const 8 - i32.add - call $basic_wallet::bindings::miden::base::tx_kernel::add_asset::wit_import - local.get 5 - i32.const 48 - i32.add - global.set $__stack_pointer - ) - (func $miden:basic-wallet/basic-wallet@1.0.0#send-asset (;5;) (type 1) (param i32 i64 i64 i64 i64 i64 i64 i64 i64 i64) - (local i32 i64 i32) - global.get $__stack_pointer - i32.const 48 - i32.sub - local.tee 10 - global.set $__stack_pointer - i64.const 0 - local.set 11 - i32.const 0 - local.set 12 - call $wit_bindgen::rt::run_ctors_once - local.get 0 - i32.const 0 - i32.ne - local.get 1 - local.get 2 - local.get 3 - i64.const 0 - local.get 0 - select - local.get 4 - i64.const 0 - local.get 0 - select - local.get 10 - i32.const 8 - i32.add - call $basic_wallet::bindings::miden::base::tx_kernel::remove_asset::wit_import - block ;; label = @1 - block ;; label = @2 - local.get 10 - i32.load8_u offset=8 - br_if 0 (;@2;) - local.get 10 - i32.const 24 - i32.add - i64.load - local.set 4 - local.get 10 - i32.const 16 - i32.add - i64.load - local.set 3 - i64.const 0 - local.set 2 - br 1 (;@1;) - end - local.get 10 - i32.const 24 - i32.add - i64.load - local.set 4 - local.get 10 - i32.const 16 - i32.add - i64.load - local.set 3 - local.get 10 - i32.const 40 - i32.add - i64.load - local.set 11 - local.get 10 - i32.const 32 - i32.add - i64.load - local.set 2 - i32.const 1 - local.set 12 - end - local.get 12 - local.get 3 - local.get 4 - local.get 2 - local.get 11 - local.get 5 - local.get 6 - local.get 7 - local.get 8 - local.get 9 - call $basic_wallet::bindings::miden::base::tx_kernel::create_note::wit_import - local.get 10 - i32.const 48 - i32.add - global.set $__stack_pointer - ) - (func $__rust_alloc (;6;) (type 4) (param i32 i32) (result i32) - (local i32) - local.get 0 - local.get 1 - call $__rdl_alloc - local.set 2 - local.get 2 - return - ) - (func $__rust_realloc (;7;) (type 5) (param i32 i32 i32 i32) (result i32) - (local i32) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - call $__rdl_realloc - local.set 4 - local.get 4 - return - ) - (func $wit_bindgen::rt::run_ctors_once (;8;) (type 2) - block ;; label = @1 - i32.const 0 - i32.load8_u offset=1048577 - br_if 0 (;@1;) - call $__wasm_call_ctors - i32.const 0 - i32.const 1 - i32.store8 offset=1048577 - end - ) - (func $cabi_realloc (;9;) (type 5) (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=1048576 - 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 - local.get 3 - call $__rust_realloc - local.set 2 - end - local.get 2 - br_if 0 (;@1;) - unreachable - unreachable - end - local.get 2 - ) - (func $__rdl_alloc (;10;) (type 4) (param i32 i32) (result i32) - (local i32) - block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.const 8 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - local.get 0 - i32.le_u - br_if 1 (;@1;) - end - local.get 1 - local.get 1 - local.get 0 - local.get 1 - i32.rem_u - local.tee 2 - i32.sub - i32.const 0 - local.get 2 - select - local.get 0 - i32.add - call $aligned_alloc - return - end - local.get 0 - call $malloc - ) - (func $__rdl_realloc (;11;) (type 5) (param i32 i32 i32 i32) (result i32) - (local i32 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 8 - i32.gt_u - br_if 0 (;@3;) - local.get 2 - local.get 3 - i32.le_u - br_if 1 (;@2;) - end - i32.const 0 - local.set 4 - local.get 2 - local.get 2 - local.get 3 - local.get 2 - i32.rem_u - local.tee 5 - i32.sub - i32.const 0 - local.get 5 - select - local.get 3 - i32.add - call $aligned_alloc - local.tee 2 - i32.eqz - br_if 1 (;@1;) - local.get 2 - local.get 0 - local.get 1 - local.get 3 - local.get 1 - local.get 3 - i32.lt_u - select - call $memcpy - local.set 2 - local.get 0 - call $free - local.get 2 - return - end - local.get 0 - local.get 3 - call $realloc - local.set 4 - end - local.get 4 - ) - (func $malloc (;12;) (type 6) (param i32) (result i32) - local.get 0 - call $dlmalloc - ) - (func $dlmalloc (;13;) (type 6) (param i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - global.get $__stack_pointer - i32.const 16 - i32.sub - local.tee 1 - global.set $__stack_pointer - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - block ;; label = @12 - i32.const 0 - i32.load offset=1048604 - local.tee 2 - br_if 0 (;@12;) - block ;; label = @13 - i32.const 0 - i32.load offset=1049052 - local.tee 3 - br_if 0 (;@13;) - i32.const 0 - i64.const -1 - i64.store offset=1049064 align=4 - i32.const 0 - i64.const 281474976776192 - i64.store offset=1049056 align=4 - i32.const 0 - local.get 1 - i32.const 8 - i32.add - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - local.tee 3 - i32.store offset=1049052 - i32.const 0 - i32.const 0 - i32.store offset=1049072 - i32.const 0 - i32.const 0 - i32.store offset=1049024 - end - i32.const 1114112 - i32.const 1049088 - i32.lt_u - br_if 1 (;@11;) - i32.const 0 - local.set 2 - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.const 89 - i32.lt_u - br_if 0 (;@12;) - i32.const 0 - local.set 4 - i32.const 0 - i32.const 1049088 - i32.store offset=1049028 - i32.const 0 - i32.const 1049088 - i32.store offset=1048596 - i32.const 0 - local.get 3 - i32.store offset=1048616 - i32.const 0 - i32.const -1 - i32.store offset=1048612 - i32.const 0 - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.store offset=1049032 - loop ;; label = @13 - local.get 4 - i32.const 1048640 - i32.add - local.get 4 - i32.const 1048628 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 4 - i32.const 1048620 - i32.add - local.tee 5 - i32.store - local.get 4 - i32.const 1048632 - i32.add - local.get 5 - i32.store - local.get 4 - i32.const 1048648 - i32.add - local.get 4 - i32.const 1048636 - i32.add - local.tee 5 - i32.store - local.get 5 - local.get 3 - i32.store - local.get 4 - i32.const 1048656 - i32.add - local.get 4 - i32.const 1048644 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 5 - i32.store - local.get 4 - i32.const 1048652 - i32.add - local.get 3 - i32.store - local.get 4 - i32.const 32 - i32.add - local.tee 4 - i32.const 256 - i32.ne - br_if 0 (;@13;) - end - i32.const 1049088 - i32.const -8 - i32.const 1049088 - i32.sub - i32.const 15 - i32.and - i32.const 0 - i32.const 1049088 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 2 - i32.const 4 - i32.add - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.const -56 - i32.add - local.tee 3 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 2 - i32.store offset=1048604 - local.get 3 - i32.const 1049088 - i32.add - i32.const 4 - i32.add - i32.const 56 - i32.store - end - block ;; label = @12 - block ;; label = @13 - local.get 0 - i32.const 236 - i32.gt_u - br_if 0 (;@13;) - block ;; label = @14 - i32.const 0 - i32.load offset=1048580 - local.tee 6 - i32.const 16 - local.get 0 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 0 - i32.const 11 - i32.lt_u - select - local.tee 7 - i32.const 3 - i32.shr_u - local.tee 3 - i32.shr_u - local.tee 4 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@14;) - block ;; label = @15 - block ;; label = @16 - local.get 4 - i32.const 1 - i32.and - local.get 3 - i32.or - i32.const 1 - i32.xor - local.tee 5 - i32.const 3 - i32.shl - local.tee 3 - i32.const 1048620 - i32.add - local.tee 4 - local.get 3 - i32.const 1048628 - i32.add - i32.load - local.tee 3 - i32.load offset=8 - local.tee 7 - i32.ne - br_if 0 (;@16;) - i32.const 0 - local.get 6 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 1 (;@15;) - end - local.get 4 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 4 - i32.store offset=12 - end - local.get 3 - i32.const 8 - i32.add - local.set 4 - local.get 3 - local.get 5 - i32.const 3 - i32.shl - local.tee 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 5 - i32.add - local.tee 3 - local.get 3 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 13 (;@1;) - end - local.get 7 - i32.const 0 - i32.load offset=1048588 - local.tee 8 - i32.le_u - br_if 1 (;@12;) - block ;; label = @14 - local.get 4 - i32.eqz - br_if 0 (;@14;) - block ;; label = @15 - block ;; label = @16 - local.get 4 - local.get 3 - i32.shl - i32.const 2 - local.get 3 - i32.shl - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.or - i32.and - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.ctz - local.tee 3 - i32.const 3 - i32.shl - local.tee 4 - i32.const 1048620 - i32.add - local.tee 5 - local.get 4 - i32.const 1048628 - i32.add - i32.load - local.tee 4 - i32.load offset=8 - local.tee 0 - i32.ne - br_if 0 (;@16;) - i32.const 0 - local.get 6 - i32.const -2 - local.get 3 - i32.rotl - i32.and - local.tee 6 - i32.store offset=1048580 - br 1 (;@15;) - end - local.get 5 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 5 - i32.store offset=12 - end - local.get 4 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - i32.const 3 - i32.shl - local.tee 3 - i32.add - local.get 3 - local.get 7 - i32.sub - local.tee 5 - i32.store - local.get 4 - local.get 7 - i32.add - local.tee 0 - local.get 5 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @15 - local.get 8 - i32.eqz - br_if 0 (;@15;) - local.get 8 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 7 - i32.const 0 - i32.load offset=1048600 - local.set 3 - block ;; label = @16 - block ;; label = @17 - local.get 6 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 9 - i32.and - br_if 0 (;@17;) - i32.const 0 - local.get 6 - local.get 9 - i32.or - i32.store offset=1048580 - local.get 7 - local.set 9 - br 1 (;@16;) - end - local.get 7 - i32.load offset=8 - local.set 9 - end - local.get 9 - local.get 3 - i32.store offset=12 - local.get 7 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 7 - i32.store offset=12 - local.get 3 - local.get 9 - i32.store offset=8 - end - local.get 4 - i32.const 8 - i32.add - local.set 4 - i32.const 0 - local.get 0 - i32.store offset=1048600 - i32.const 0 - local.get 5 - i32.store offset=1048588 - br 13 (;@1;) - end - i32.const 0 - i32.load offset=1048584 - local.tee 10 - i32.eqz - br_if 1 (;@12;) - local.get 10 - i32.const 0 - local.get 10 - i32.sub - i32.and - i32.ctz - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.tee 0 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.set 3 - local.get 0 - local.set 5 - block ;; label = @14 - loop ;; label = @15 - block ;; label = @16 - local.get 5 - i32.load offset=16 - local.tee 4 - br_if 0 (;@16;) - local.get 5 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 2 (;@14;) - end - local.get 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 5 - local.get 3 - local.get 5 - local.get 3 - i32.lt_u - local.tee 5 - select - local.set 3 - local.get 4 - local.get 0 - local.get 5 - select - local.set 0 - local.get 4 - local.set 5 - br 0 (;@15;) - end - end - local.get 0 - i32.load offset=24 - local.set 11 - block ;; label = @14 - local.get 0 - i32.load offset=12 - local.tee 9 - local.get 0 - i32.eq - br_if 0 (;@14;) - local.get 0 - i32.load offset=8 - local.tee 4 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 9 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 9 - i32.store offset=12 - br 12 (;@2;) - end - block ;; label = @14 - local.get 0 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@14;) - local.get 0 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 4 (;@10;) - local.get 0 - i32.const 16 - i32.add - local.set 5 - end - loop ;; label = @14 - local.get 5 - local.set 2 - local.get 4 - local.tee 9 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@14;) - local.get 9 - i32.const 16 - i32.add - local.set 5 - local.get 9 - i32.load offset=16 - local.tee 4 - br_if 0 (;@14;) - end - local.get 2 - i32.const 0 - i32.store - br 11 (;@2;) - end - i32.const -1 - local.set 7 - local.get 0 - i32.const -65 - i32.gt_u - br_if 0 (;@12;) - local.get 0 - i32.const 19 - i32.add - local.tee 4 - i32.const -16 - i32.and - local.set 7 - i32.const 0 - i32.load offset=1048584 - local.tee 10 - i32.eqz - br_if 0 (;@12;) - i32.const 0 - local.set 8 - block ;; label = @13 - local.get 7 - i32.const 256 - i32.lt_u - br_if 0 (;@13;) - i32.const 31 - local.set 8 - local.get 7 - i32.const 16777215 - i32.gt_u - br_if 0 (;@13;) - local.get 7 - i32.const 38 - local.get 4 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 8 - end - i32.const 0 - local.get 7 - i32.sub - local.set 3 - block ;; label = @13 - block ;; label = @14 - block ;; label = @15 - block ;; label = @16 - local.get 8 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.tee 5 - br_if 0 (;@16;) - i32.const 0 - local.set 4 - i32.const 0 - local.set 9 - br 1 (;@15;) - end - i32.const 0 - local.set 4 - local.get 7 - i32.const 0 - i32.const 25 - local.get 8 - i32.const 1 - i32.shr_u - i32.sub - local.get 8 - i32.const 31 - i32.eq - select - i32.shl - local.set 0 - i32.const 0 - local.set 9 - loop ;; label = @16 - block ;; label = @17 - local.get 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 6 - local.get 3 - i32.ge_u - br_if 0 (;@17;) - local.get 6 - local.set 3 - local.get 5 - local.set 9 - local.get 6 - br_if 0 (;@17;) - i32.const 0 - local.set 3 - local.get 5 - local.set 9 - local.get 5 - local.set 4 - br 3 (;@14;) - end - local.get 4 - local.get 5 - i32.const 20 - i32.add - i32.load - local.tee 6 - local.get 6 - local.get 5 - local.get 0 - i32.const 29 - i32.shr_u - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - i32.load - local.tee 5 - i32.eq - select - local.get 4 - local.get 6 - select - local.set 4 - local.get 0 - i32.const 1 - i32.shl - local.set 0 - local.get 5 - br_if 0 (;@16;) - end - end - block ;; label = @15 - local.get 4 - local.get 9 - i32.or - br_if 0 (;@15;) - i32.const 0 - local.set 9 - i32.const 2 - local.get 8 - i32.shl - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.or - local.get 10 - i32.and - local.tee 4 - i32.eqz - br_if 3 (;@12;) - local.get 4 - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.ctz - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.set 4 - end - local.get 4 - i32.eqz - br_if 1 (;@13;) - end - loop ;; label = @14 - local.get 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 6 - local.get 3 - i32.lt_u - local.set 0 - block ;; label = @15 - local.get 4 - i32.load offset=16 - local.tee 5 - br_if 0 (;@15;) - local.get 4 - i32.const 20 - i32.add - i32.load - local.set 5 - end - local.get 6 - local.get 3 - local.get 0 - select - local.set 3 - local.get 4 - local.get 9 - local.get 0 - select - local.set 9 - local.get 5 - local.set 4 - local.get 5 - br_if 0 (;@14;) - end - end - local.get 9 - i32.eqz - br_if 0 (;@12;) - local.get 3 - i32.const 0 - i32.load offset=1048588 - local.get 7 - i32.sub - i32.ge_u - br_if 0 (;@12;) - local.get 9 - i32.load offset=24 - local.set 2 - block ;; label = @13 - local.get 9 - i32.load offset=12 - local.tee 0 - local.get 9 - i32.eq - br_if 0 (;@13;) - local.get 9 - i32.load offset=8 - local.tee 4 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 0 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 0 - i32.store offset=12 - br 10 (;@3;) - end - block ;; label = @13 - local.get 9 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@13;) - local.get 9 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 4 (;@9;) - local.get 9 - i32.const 16 - i32.add - local.set 5 - end - loop ;; label = @13 - local.get 5 - local.set 6 - local.get 4 - local.tee 0 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@13;) - local.get 0 - i32.const 16 - i32.add - local.set 5 - local.get 0 - i32.load offset=16 - local.tee 4 - br_if 0 (;@13;) - end - local.get 6 - i32.const 0 - i32.store - br 9 (;@3;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1048588 - local.tee 4 - local.get 7 - i32.lt_u - br_if 0 (;@12;) - i32.const 0 - i32.load offset=1048600 - local.set 3 - block ;; label = @13 - block ;; label = @14 - local.get 4 - local.get 7 - i32.sub - local.tee 5 - i32.const 16 - i32.lt_u - br_if 0 (;@14;) - local.get 3 - local.get 7 - i32.add - local.tee 0 - local.get 5 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 4 - i32.add - local.get 5 - i32.store - local.get 3 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - br 1 (;@13;) - end - local.get 3 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 0 - i32.const 0 - local.set 5 - end - i32.const 0 - local.get 5 - i32.store offset=1048588 - i32.const 0 - local.get 0 - i32.store offset=1048600 - local.get 3 - i32.const 8 - i32.add - local.set 4 - br 11 (;@1;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1048592 - local.tee 5 - local.get 7 - i32.le_u - br_if 0 (;@12;) - local.get 2 - local.get 7 - i32.add - local.tee 4 - local.get 5 - local.get 7 - i32.sub - local.tee 3 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.get 4 - i32.store offset=1048604 - i32.const 0 - local.get 3 - i32.store offset=1048592 - local.get 2 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 2 - i32.const 8 - i32.add - local.set 4 - br 11 (;@1;) - end - block ;; label = @12 - block ;; label = @13 - i32.const 0 - i32.load offset=1049052 - i32.eqz - br_if 0 (;@13;) - i32.const 0 - i32.load offset=1049060 - local.set 3 - br 1 (;@12;) - end - i32.const 0 - i64.const -1 - i64.store offset=1049064 align=4 - i32.const 0 - i64.const 281474976776192 - i64.store offset=1049056 align=4 - i32.const 0 - local.get 1 - i32.const 12 - i32.add - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - i32.store offset=1049052 - i32.const 0 - i32.const 0 - i32.store offset=1049072 - i32.const 0 - i32.const 0 - i32.store offset=1049024 - i32.const 65536 - local.set 3 - end - i32.const 0 - local.set 4 - block ;; label = @12 - local.get 3 - local.get 7 - i32.const 71 - i32.add - local.tee 8 - i32.add - local.tee 0 - i32.const 0 - local.get 3 - i32.sub - local.tee 6 - i32.and - local.tee 9 - local.get 7 - i32.gt_u - br_if 0 (;@12;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 11 (;@1;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1049020 - local.tee 4 - i32.eqz - br_if 0 (;@12;) - block ;; label = @13 - i32.const 0 - i32.load offset=1049012 - local.tee 3 - local.get 9 - i32.add - local.tee 10 - local.get 3 - i32.le_u - br_if 0 (;@13;) - local.get 10 - local.get 4 - i32.le_u - br_if 1 (;@12;) - end - i32.const 0 - local.set 4 - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 11 (;@1;) - end - i32.const 0 - i32.load8_u offset=1049024 - i32.const 4 - i32.and - br_if 5 (;@6;) - block ;; label = @12 - block ;; label = @13 - block ;; label = @14 - local.get 2 - i32.eqz - br_if 0 (;@14;) - i32.const 1049028 - local.set 4 - loop ;; label = @15 - block ;; label = @16 - local.get 4 - i32.load - local.tee 3 - local.get 2 - i32.gt_u - br_if 0 (;@16;) - local.get 3 - local.get 4 - i32.load offset=4 - i32.add - local.get 2 - i32.gt_u - br_if 3 (;@13;) - end - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@15;) - end - end - i32.const 0 - call $sbrk - local.tee 0 - i32.const -1 - i32.eq - br_if 6 (;@7;) - local.get 9 - local.set 6 - block ;; label = @14 - i32.const 0 - i32.load offset=1049056 - local.tee 4 - i32.const -1 - i32.add - local.tee 3 - local.get 0 - i32.and - i32.eqz - br_if 0 (;@14;) - local.get 9 - local.get 0 - i32.sub - local.get 3 - local.get 0 - i32.add - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.add - local.set 6 - end - local.get 6 - local.get 7 - i32.le_u - br_if 6 (;@7;) - local.get 6 - i32.const 2147483646 - i32.gt_u - br_if 6 (;@7;) - block ;; label = @14 - i32.const 0 - i32.load offset=1049020 - local.tee 4 - i32.eqz - br_if 0 (;@14;) - i32.const 0 - i32.load offset=1049012 - local.tee 3 - local.get 6 - i32.add - local.tee 5 - local.get 3 - i32.le_u - br_if 7 (;@7;) - local.get 5 - local.get 4 - i32.gt_u - br_if 7 (;@7;) - end - local.get 6 - call $sbrk - local.tee 4 - local.get 0 - i32.ne - br_if 1 (;@12;) - br 8 (;@5;) - end - local.get 0 - local.get 5 - i32.sub - local.get 6 - i32.and - local.tee 6 - i32.const 2147483646 - i32.gt_u - br_if 5 (;@7;) - local.get 6 - call $sbrk - local.tee 0 - local.get 4 - i32.load - local.get 4 - i32.load offset=4 - i32.add - i32.eq - br_if 4 (;@8;) - local.get 0 - local.set 4 - end - block ;; label = @12 - local.get 4 - i32.const -1 - i32.eq - br_if 0 (;@12;) - local.get 7 - i32.const 72 - i32.add - local.get 6 - i32.le_u - br_if 0 (;@12;) - block ;; label = @13 - local.get 8 - local.get 6 - i32.sub - i32.const 0 - i32.load offset=1049060 - local.tee 3 - i32.add - i32.const 0 - local.get 3 - i32.sub - i32.and - local.tee 3 - i32.const 2147483646 - i32.le_u - br_if 0 (;@13;) - local.get 4 - local.set 0 - br 8 (;@5;) - end - block ;; label = @13 - local.get 3 - call $sbrk - i32.const -1 - i32.eq - br_if 0 (;@13;) - local.get 3 - local.get 6 - i32.add - local.set 6 - local.get 4 - local.set 0 - br 8 (;@5;) - end - i32.const 0 - local.get 6 - i32.sub - call $sbrk - drop - br 5 (;@7;) - end - local.get 4 - local.set 0 - local.get 4 - i32.const -1 - i32.ne - br_if 6 (;@5;) - br 4 (;@7;) - end - unreachable - unreachable - end - i32.const 0 - local.set 9 - br 7 (;@2;) - end - i32.const 0 - local.set 0 - br 5 (;@3;) - end - local.get 0 - i32.const -1 - i32.ne - br_if 2 (;@5;) - end - i32.const 0 - i32.const 0 - i32.load offset=1049024 - i32.const 4 - i32.or - i32.store offset=1049024 - end - local.get 9 - i32.const 2147483646 - i32.gt_u - br_if 1 (;@4;) - local.get 9 - call $sbrk - local.set 0 - i32.const 0 - call $sbrk - local.set 4 - local.get 0 - i32.const -1 - i32.eq - br_if 1 (;@4;) - local.get 4 - i32.const -1 - i32.eq - br_if 1 (;@4;) - local.get 0 - local.get 4 - i32.ge_u - br_if 1 (;@4;) - local.get 4 - local.get 0 - i32.sub - local.tee 6 - local.get 7 - i32.const 56 - i32.add - i32.le_u - br_if 1 (;@4;) - end - i32.const 0 - i32.const 0 - i32.load offset=1049012 - local.get 6 - i32.add - local.tee 4 - i32.store offset=1049012 - block ;; label = @5 - local.get 4 - i32.const 0 - i32.load offset=1049016 - i32.le_u - br_if 0 (;@5;) - i32.const 0 - local.get 4 - i32.store offset=1049016 - end - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - i32.const 0 - i32.load offset=1048604 - local.tee 3 - i32.eqz - br_if 0 (;@8;) - i32.const 1049028 - local.set 4 - loop ;; label = @9 - local.get 0 - local.get 4 - i32.load - local.tee 5 - local.get 4 - i32.load offset=4 - local.tee 9 - i32.add - i32.eq - br_if 2 (;@7;) - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@9;) - br 3 (;@6;) - end - end - block ;; label = @8 - block ;; label = @9 - i32.const 0 - i32.load offset=1048596 - local.tee 4 - i32.eqz - br_if 0 (;@9;) - local.get 0 - local.get 4 - i32.ge_u - br_if 1 (;@8;) - end - i32.const 0 - local.get 0 - i32.store offset=1048596 - end - i32.const 0 - local.set 4 - i32.const 0 - local.get 6 - i32.store offset=1049032 - i32.const 0 - local.get 0 - i32.store offset=1049028 - i32.const 0 - i32.const -1 - i32.store offset=1048612 - i32.const 0 - i32.const 0 - i32.load offset=1049052 - i32.store offset=1048616 - i32.const 0 - i32.const 0 - i32.store offset=1049040 - loop ;; label = @8 - local.get 4 - i32.const 1048640 - i32.add - local.get 4 - i32.const 1048628 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 4 - i32.const 1048620 - i32.add - local.tee 5 - i32.store - local.get 4 - i32.const 1048632 - i32.add - local.get 5 - i32.store - local.get 4 - i32.const 1048648 - i32.add - local.get 4 - i32.const 1048636 - i32.add - local.tee 5 - i32.store - local.get 5 - local.get 3 - i32.store - local.get 4 - i32.const 1048656 - i32.add - local.get 4 - i32.const 1048644 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 5 - i32.store - local.get 4 - i32.const 1048652 - i32.add - local.get 3 - i32.store - local.get 4 - i32.const 32 - i32.add - local.tee 4 - i32.const 256 - i32.ne - br_if 0 (;@8;) - end - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 3 - local.get 6 - i32.const -56 - i32.add - local.tee 5 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 3 - i32.store offset=1048604 - local.get 0 - local.get 5 - i32.add - i32.const 56 - i32.store offset=4 - br 2 (;@5;) - end - local.get 4 - i32.load8_u offset=12 - i32.const 8 - i32.and - br_if 0 (;@6;) - local.get 3 - local.get 5 - i32.lt_u - br_if 0 (;@6;) - local.get 3 - local.get 0 - i32.ge_u - br_if 0 (;@6;) - local.get 3 - i32.const -8 - local.get 3 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 3 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 5 - i32.add - local.tee 0 - i32.const 0 - i32.load offset=1048592 - local.get 6 - i32.add - local.tee 2 - local.get 5 - i32.sub - local.tee 5 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 9 - local.get 6 - i32.add - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 5 - i32.store offset=1048592 - i32.const 0 - local.get 0 - i32.store offset=1048604 - local.get 3 - local.get 2 - i32.add - i32.const 56 - i32.store offset=4 - br 1 (;@5;) - end - block ;; label = @6 - local.get 0 - i32.const 0 - i32.load offset=1048596 - local.tee 9 - i32.ge_u - br_if 0 (;@6;) - i32.const 0 - local.get 0 - i32.store offset=1048596 - local.get 0 - local.set 9 - end - local.get 0 - local.get 6 - i32.add - local.set 5 - i32.const 1049028 - local.set 4 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - block ;; label = @12 - loop ;; label = @13 - local.get 4 - i32.load - local.get 5 - i32.eq - br_if 1 (;@12;) - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@13;) - br 2 (;@11;) - end - end - local.get 4 - i32.load8_u offset=12 - i32.const 8 - i32.and - i32.eqz - br_if 1 (;@10;) - end - i32.const 1049028 - local.set 4 - loop ;; label = @11 - block ;; label = @12 - local.get 4 - i32.load - local.tee 5 - local.get 3 - i32.gt_u - br_if 0 (;@12;) - local.get 5 - local.get 4 - i32.load offset=4 - i32.add - local.tee 5 - local.get 3 - i32.gt_u - br_if 3 (;@9;) - end - local.get 4 - i32.load offset=8 - local.set 4 - br 0 (;@11;) - end - end - local.get 4 - local.get 0 - i32.store - local.get 4 - local.get 4 - i32.load offset=4 - local.get 6 - i32.add - i32.store offset=4 - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - i32.add - local.tee 2 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 5 - i32.const -8 - local.get 5 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 5 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - i32.add - local.tee 6 - local.get 2 - local.get 7 - i32.add - local.tee 7 - i32.sub - local.set 4 - block ;; label = @10 - local.get 6 - local.get 3 - i32.ne - br_if 0 (;@10;) - i32.const 0 - local.get 7 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 4 - i32.add - local.tee 4 - i32.store offset=1048592 - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - br 3 (;@7;) - end - block ;; label = @10 - local.get 6 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@10;) - i32.const 0 - local.get 7 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 4 - i32.add - local.tee 4 - i32.store offset=1048588 - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 7 - local.get 4 - i32.add - local.get 4 - i32.store - br 3 (;@7;) - end - block ;; label = @10 - local.get 6 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 1 - i32.ne - br_if 0 (;@10;) - local.get 3 - i32.const -8 - i32.and - local.set 8 - block ;; label = @11 - block ;; label = @12 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@12;) - local.get 6 - i32.load offset=8 - local.tee 5 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 9 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 0 - i32.eq - drop - block ;; label = @13 - local.get 6 - i32.load offset=12 - local.tee 3 - local.get 5 - i32.ne - br_if 0 (;@13;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 9 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@11;) - end - local.get 3 - local.get 0 - i32.eq - drop - local.get 3 - local.get 5 - i32.store offset=8 - local.get 5 - local.get 3 - i32.store offset=12 - br 1 (;@11;) - end - local.get 6 - i32.load offset=24 - local.set 10 - block ;; label = @12 - block ;; label = @13 - local.get 6 - i32.load offset=12 - local.tee 0 - local.get 6 - i32.eq - br_if 0 (;@13;) - local.get 6 - i32.load offset=8 - local.tee 3 - local.get 9 - i32.lt_u - drop - local.get 0 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 0 - i32.store offset=12 - br 1 (;@12;) - end - block ;; label = @13 - local.get 6 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - local.get 6 - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - i32.const 0 - local.set 0 - br 1 (;@12;) - end - loop ;; label = @13 - local.get 3 - local.set 9 - local.get 5 - local.tee 0 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - local.get 0 - i32.const 16 - i32.add - local.set 3 - local.get 0 - i32.load offset=16 - local.tee 5 - br_if 0 (;@13;) - end - local.get 9 - i32.const 0 - i32.store - end - local.get 10 - i32.eqz - br_if 0 (;@11;) - block ;; label = @12 - block ;; label = @13 - local.get 6 - local.get 6 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@13;) - local.get 3 - local.get 0 - i32.store - local.get 0 - br_if 1 (;@12;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@11;) - end - local.get 10 - i32.const 16 - i32.const 20 - local.get 10 - i32.load offset=16 - local.get 6 - i32.eq - select - i32.add - local.get 0 - i32.store - local.get 0 - i32.eqz - br_if 1 (;@11;) - end - local.get 0 - local.get 10 - i32.store offset=24 - block ;; label = @12 - local.get 6 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@12;) - local.get 0 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 0 - i32.store offset=24 - end - local.get 6 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 0 (;@11;) - local.get 0 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 0 - i32.store offset=24 - end - local.get 8 - local.get 4 - i32.add - local.set 4 - local.get 6 - local.get 8 - i32.add - local.tee 6 - i32.load offset=4 - local.set 3 - end - local.get 6 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 7 - local.get 4 - i32.add - local.get 4 - i32.store - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @10 - local.get 4 - i32.const 255 - i32.gt_u - br_if 0 (;@10;) - local.get 4 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 3 - block ;; label = @11 - block ;; label = @12 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 4 - i32.const 3 - i32.shr_u - i32.shl - local.tee 4 - i32.and - br_if 0 (;@12;) - i32.const 0 - local.get 5 - local.get 4 - i32.or - i32.store offset=1048580 - local.get 3 - local.set 4 - br 1 (;@11;) - end - local.get 3 - i32.load offset=8 - local.set 4 - end - local.get 4 - local.get 7 - i32.store offset=12 - local.get 3 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 3 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - br 3 (;@7;) - end - i32.const 31 - local.set 3 - block ;; label = @10 - local.get 4 - i32.const 16777215 - i32.gt_u - br_if 0 (;@10;) - local.get 4 - i32.const 38 - local.get 4 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 7 - local.get 3 - i32.store offset=28 - local.get 7 - i64.const 0 - i64.store offset=16 align=4 - local.get 3 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @10 - i32.const 0 - i32.load offset=1048584 - local.tee 0 - i32.const 1 - local.get 3 - i32.shl - local.tee 9 - i32.and - br_if 0 (;@10;) - local.get 5 - local.get 7 - i32.store - i32.const 0 - local.get 0 - local.get 9 - i32.or - i32.store offset=1048584 - local.get 7 - local.get 5 - i32.store offset=24 - local.get 7 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 7 - i32.store offset=12 - br 3 (;@7;) - end - local.get 4 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 5 - i32.load - local.set 0 - loop ;; label = @10 - local.get 0 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 4 - i32.eq - br_if 2 (;@8;) - local.get 3 - i32.const 29 - i32.shr_u - local.set 0 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 5 - local.get 0 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 9 - i32.load - local.tee 0 - br_if 0 (;@10;) - end - local.get 9 - local.get 7 - i32.store - local.get 7 - local.get 5 - i32.store offset=24 - local.get 7 - local.get 7 - i32.store offset=12 - local.get 7 - local.get 7 - i32.store offset=8 - br 2 (;@7;) - end - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 2 - local.get 6 - i32.const -56 - i32.add - local.tee 9 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 9 - i32.add - i32.const 56 - i32.store offset=4 - local.get 3 - local.get 5 - i32.const 55 - local.get 5 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 5 - i32.const -55 - i32.add - i32.const 15 - i32.and - select - i32.add - i32.const -63 - i32.add - local.tee 9 - local.get 9 - local.get 3 - i32.const 16 - i32.add - i32.lt_u - select - local.tee 9 - i32.const 35 - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 2 - i32.store offset=1048604 - local.get 9 - i32.const 16 - i32.add - i32.const 0 - i64.load offset=1049036 align=4 - i64.store align=4 - local.get 9 - i32.const 0 - i64.load offset=1049028 align=4 - i64.store offset=8 align=4 - i32.const 0 - local.get 9 - i32.const 8 - i32.add - i32.store offset=1049036 - i32.const 0 - local.get 6 - i32.store offset=1049032 - i32.const 0 - local.get 0 - i32.store offset=1049028 - i32.const 0 - i32.const 0 - i32.store offset=1049040 - local.get 9 - i32.const 36 - i32.add - local.set 4 - loop ;; label = @9 - local.get 4 - i32.const 7 - i32.store - local.get 4 - i32.const 4 - i32.add - local.tee 4 - local.get 5 - i32.lt_u - br_if 0 (;@9;) - end - local.get 9 - local.get 3 - i32.eq - br_if 3 (;@5;) - local.get 9 - local.get 9 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - local.get 9 - local.get 9 - local.get 3 - i32.sub - local.tee 0 - i32.store - local.get 3 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @9 - local.get 0 - i32.const 255 - i32.gt_u - br_if 0 (;@9;) - local.get 0 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 4 - block ;; label = @10 - block ;; label = @11 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 0 - i32.const 3 - i32.shr_u - i32.shl - local.tee 0 - i32.and - br_if 0 (;@11;) - i32.const 0 - local.get 5 - local.get 0 - i32.or - i32.store offset=1048580 - local.get 4 - local.set 5 - br 1 (;@10;) - end - local.get 4 - i32.load offset=8 - local.set 5 - end - local.get 5 - local.get 3 - i32.store offset=12 - local.get 4 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 4 - i32.store offset=12 - local.get 3 - local.get 5 - i32.store offset=8 - br 4 (;@5;) - end - i32.const 31 - local.set 4 - block ;; label = @9 - local.get 0 - i32.const 16777215 - i32.gt_u - br_if 0 (;@9;) - local.get 0 - i32.const 38 - local.get 0 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 4 - end - local.get 3 - local.get 4 - i32.store offset=28 - local.get 3 - i64.const 0 - i64.store offset=16 align=4 - local.get 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @9 - i32.const 0 - i32.load offset=1048584 - local.tee 9 - i32.const 1 - local.get 4 - i32.shl - local.tee 6 - i32.and - br_if 0 (;@9;) - local.get 5 - local.get 3 - i32.store - i32.const 0 - local.get 9 - local.get 6 - i32.or - i32.store offset=1048584 - local.get 3 - local.get 5 - i32.store offset=24 - local.get 3 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 3 - i32.store offset=12 - br 4 (;@5;) - end - local.get 0 - i32.const 0 - i32.const 25 - local.get 4 - i32.const 1 - i32.shr_u - i32.sub - local.get 4 - i32.const 31 - i32.eq - select - i32.shl - local.set 4 - local.get 5 - i32.load - local.set 9 - loop ;; label = @9 - local.get 9 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 0 - i32.eq - br_if 3 (;@6;) - local.get 4 - i32.const 29 - i32.shr_u - local.set 9 - local.get 4 - i32.const 1 - i32.shl - local.set 4 - local.get 5 - local.get 9 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 6 - i32.load - local.tee 9 - br_if 0 (;@9;) - end - local.get 6 - local.get 3 - i32.store - local.get 3 - local.get 5 - i32.store offset=24 - local.get 3 - local.get 3 - i32.store offset=12 - local.get 3 - local.get 3 - i32.store offset=8 - br 3 (;@5;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 7 - i32.store offset=12 - local.get 5 - local.get 7 - i32.store offset=8 - local.get 7 - i32.const 0 - i32.store offset=24 - local.get 7 - local.get 5 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - end - local.get 2 - i32.const 8 - i32.add - local.set 4 - br 5 (;@1;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.store offset=12 - local.get 5 - local.get 3 - i32.store offset=8 - local.get 3 - i32.const 0 - i32.store offset=24 - local.get 3 - local.get 5 - i32.store offset=12 - local.get 3 - local.get 4 - i32.store offset=8 - end - i32.const 0 - i32.load offset=1048592 - local.tee 4 - local.get 7 - i32.le_u - br_if 0 (;@4;) - i32.const 0 - i32.load offset=1048604 - local.tee 3 - local.get 7 - i32.add - local.tee 5 - local.get 4 - local.get 7 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 5 - i32.store offset=1048604 - local.get 3 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - i32.const 8 - i32.add - local.set 4 - br 3 (;@1;) - end - i32.const 0 - local.set 4 - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 2 (;@1;) - end - block ;; label = @3 - local.get 2 - i32.eqz - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 9 - local.get 9 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 4 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 4 - local.get 0 - i32.store - local.get 0 - br_if 1 (;@4;) - i32.const 0 - local.get 10 - i32.const -2 - local.get 5 - i32.rotl - i32.and - local.tee 10 - i32.store offset=1048584 - br 2 (;@3;) - end - local.get 2 - i32.const 16 - i32.const 20 - local.get 2 - i32.load offset=16 - local.get 9 - i32.eq - select - i32.add - local.get 0 - i32.store - local.get 0 - i32.eqz - br_if 1 (;@3;) - end - local.get 0 - local.get 2 - i32.store offset=24 - block ;; label = @4 - local.get 9 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 0 (;@4;) - local.get 0 - local.get 4 - i32.store offset=16 - local.get 4 - local.get 0 - i32.store offset=24 - end - local.get 9 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@3;) - local.get 0 - i32.const 20 - i32.add - local.get 4 - i32.store - local.get 4 - local.get 0 - i32.store offset=24 - end - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 15 - i32.gt_u - br_if 0 (;@4;) - local.get 9 - local.get 3 - local.get 7 - i32.add - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 9 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 1 (;@3;) - end - local.get 9 - local.get 7 - i32.add - local.tee 0 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 9 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @4 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 3 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 4 - block ;; label = @5 - block ;; label = @6 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 3 - i32.const 3 - i32.shr_u - i32.shl - local.tee 3 - i32.and - br_if 0 (;@6;) - i32.const 0 - local.get 5 - local.get 3 - i32.or - i32.store offset=1048580 - local.get 4 - local.set 3 - br 1 (;@5;) - end - local.get 4 - i32.load offset=8 - local.set 3 - end - local.get 3 - local.get 0 - i32.store offset=12 - local.get 4 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 4 - i32.store offset=12 - local.get 0 - local.get 3 - i32.store offset=8 - br 1 (;@3;) - end - i32.const 31 - local.set 4 - block ;; label = @4 - local.get 3 - i32.const 16777215 - i32.gt_u - br_if 0 (;@4;) - local.get 3 - i32.const 38 - local.get 3 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 4 - end - local.get 0 - local.get 4 - i32.store offset=28 - local.get 0 - i64.const 0 - i64.store offset=16 align=4 - local.get 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @4 - local.get 10 - i32.const 1 - local.get 4 - i32.shl - local.tee 7 - i32.and - br_if 0 (;@4;) - local.get 5 - local.get 0 - i32.store - i32.const 0 - local.get 10 - local.get 7 - i32.or - i32.store offset=1048584 - local.get 0 - local.get 5 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 0 - i32.store offset=12 - br 1 (;@3;) - end - local.get 3 - i32.const 0 - i32.const 25 - local.get 4 - i32.const 1 - i32.shr_u - i32.sub - local.get 4 - i32.const 31 - i32.eq - select - i32.shl - local.set 4 - local.get 5 - i32.load - local.set 7 - block ;; label = @4 - loop ;; label = @5 - local.get 7 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 3 - i32.eq - br_if 1 (;@4;) - local.get 4 - i32.const 29 - i32.shr_u - local.set 7 - local.get 4 - i32.const 1 - i32.shl - local.set 4 - local.get 5 - local.get 7 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 6 - i32.load - local.tee 7 - br_if 0 (;@5;) - end - local.get 6 - local.get 0 - i32.store - local.get 0 - local.get 5 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=12 - local.get 0 - local.get 0 - i32.store offset=8 - br 1 (;@3;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 0 - i32.store offset=12 - local.get 5 - local.get 0 - i32.store offset=8 - local.get 0 - i32.const 0 - i32.store offset=24 - local.get 0 - local.get 5 - i32.store offset=12 - local.get 0 - local.get 4 - i32.store offset=8 - end - local.get 9 - i32.const 8 - i32.add - local.set 4 - br 1 (;@1;) - end - block ;; label = @2 - local.get 11 - i32.eqz - br_if 0 (;@2;) - block ;; label = @3 - block ;; label = @4 - local.get 0 - local.get 0 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 4 - i32.load - i32.ne - br_if 0 (;@4;) - local.get 4 - local.get 9 - i32.store - local.get 9 - br_if 1 (;@3;) - i32.const 0 - local.get 10 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@2;) - end - local.get 11 - i32.const 16 - i32.const 20 - local.get 11 - i32.load offset=16 - local.get 0 - i32.eq - select - i32.add - local.get 9 - i32.store - local.get 9 - i32.eqz - br_if 1 (;@2;) - end - local.get 9 - local.get 11 - i32.store offset=24 - block ;; label = @3 - local.get 0 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 0 (;@3;) - local.get 9 - local.get 4 - i32.store offset=16 - local.get 4 - local.get 9 - i32.store offset=24 - end - local.get 0 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@2;) - local.get 9 - i32.const 20 - i32.add - local.get 4 - i32.store - local.get 4 - local.get 9 - i32.store offset=24 - end - block ;; label = @2 - block ;; label = @3 - local.get 3 - i32.const 15 - i32.gt_u - br_if 0 (;@3;) - local.get 0 - local.get 3 - local.get 7 - i32.add - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 1 (;@2;) - end - local.get 0 - local.get 7 - i32.add - local.tee 5 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 5 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @3 - local.get 8 - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 7 - i32.const 0 - i32.load offset=1048600 - local.set 4 - block ;; label = @4 - block ;; label = @5 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 9 - local.get 6 - i32.and - br_if 0 (;@5;) - i32.const 0 - local.get 9 - local.get 6 - i32.or - i32.store offset=1048580 - local.get 7 - local.set 9 - br 1 (;@4;) - end - local.get 7 - i32.load offset=8 - local.set 9 - end - local.get 9 - local.get 4 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 7 - i32.store offset=12 - local.get 4 - local.get 9 - i32.store offset=8 - end - i32.const 0 - local.get 5 - i32.store offset=1048600 - i32.const 0 - local.get 3 - i32.store offset=1048588 - end - local.get 0 - i32.const 8 - i32.add - local.set 4 - end - local.get 1 - i32.const 16 - i32.add - global.set $__stack_pointer - local.get 4 - ) - (func $free (;14;) (type 7) (param i32) - local.get 0 - call $dlfree - ) - (func $dlfree (;15;) (type 7) (param i32) - (local i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 0 - i32.eqz - br_if 0 (;@1;) - local.get 0 - i32.const -8 - i32.add - local.tee 1 - local.get 0 - i32.const -4 - i32.add - i32.load - local.tee 2 - i32.const -8 - i32.and - local.tee 0 - i32.add - local.set 3 - block ;; label = @2 - local.get 2 - i32.const 1 - i32.and - br_if 0 (;@2;) - local.get 2 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 1 - local.get 1 - i32.load - local.tee 2 - i32.sub - local.tee 1 - i32.const 0 - i32.load offset=1048596 - local.tee 4 - i32.lt_u - br_if 1 (;@1;) - local.get 2 - local.get 0 - i32.add - local.set 0 - block ;; label = @3 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.eq - br_if 0 (;@3;) - block ;; label = @4 - local.get 2 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 1 - i32.load offset=8 - local.tee 4 - local.get 2 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @5 - local.get 1 - i32.load offset=12 - local.tee 2 - local.get 4 - i32.ne - br_if 0 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 3 (;@2;) - end - local.get 2 - local.get 6 - i32.eq - drop - local.get 2 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 2 - i32.store offset=12 - br 2 (;@2;) - end - local.get 1 - i32.load offset=24 - local.set 7 - block ;; label = @4 - block ;; label = @5 - local.get 1 - i32.load offset=12 - local.tee 6 - local.get 1 - i32.eq - br_if 0 (;@5;) - local.get 1 - i32.load offset=8 - local.tee 2 - local.get 4 - i32.lt_u - drop - local.get 6 - local.get 2 - i32.store offset=8 - local.get 2 - local.get 6 - i32.store offset=12 - br 1 (;@4;) - end - block ;; label = @5 - local.get 1 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - local.get 1 - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - i32.const 0 - local.set 6 - br 1 (;@4;) - end - loop ;; label = @5 - local.get 2 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - local.get 6 - i32.const 16 - i32.add - local.set 2 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@5;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 1 - local.get 1 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 2 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 2 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@4;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 3 (;@2;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 1 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 2 (;@2;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @4 - local.get 1 - i32.load offset=16 - local.tee 2 - i32.eqz - br_if 0 (;@4;) - local.get 6 - local.get 2 - i32.store offset=16 - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 1 - i32.load offset=20 - local.tee 2 - i32.eqz - br_if 1 (;@2;) - local.get 6 - i32.const 20 - i32.add - local.get 2 - i32.store - local.get 2 - local.get 6 - i32.store offset=24 - br 1 (;@2;) - end - local.get 3 - i32.load offset=4 - local.tee 2 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 0 (;@2;) - local.get 3 - local.get 2 - i32.const -2 - i32.and - i32.store offset=4 - i32.const 0 - local.get 0 - i32.store offset=1048588 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - return - end - local.get 1 - local.get 3 - i32.ge_u - br_if 0 (;@1;) - local.get 3 - i32.load offset=4 - local.tee 2 - i32.const 1 - i32.and - i32.eqz - br_if 0 (;@1;) - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 2 - i32.and - br_if 0 (;@3;) - block ;; label = @4 - local.get 3 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 1 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 0 - i32.add - local.tee 0 - i32.store offset=1048592 - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 3 (;@1;) - i32.const 0 - i32.const 0 - i32.store offset=1048588 - i32.const 0 - i32.const 0 - i32.store offset=1048600 - return - end - block ;; label = @4 - local.get 3 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 1 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 0 - i32.add - local.tee 0 - i32.store offset=1048588 - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - return - end - local.get 2 - i32.const -8 - i32.and - local.get 0 - i32.add - local.set 0 - block ;; label = @4 - block ;; label = @5 - local.get 2 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 3 - i32.load offset=8 - local.tee 4 - local.get 2 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @6 - local.get 3 - i32.load offset=12 - local.tee 2 - local.get 4 - i32.ne - br_if 0 (;@6;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@4;) - end - local.get 2 - local.get 6 - i32.eq - drop - local.get 2 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 2 - i32.store offset=12 - br 1 (;@4;) - end - local.get 3 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.load offset=12 - local.tee 6 - local.get 3 - i32.eq - br_if 0 (;@6;) - local.get 3 - i32.load offset=8 - local.tee 2 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 2 - i32.store offset=8 - local.get 2 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 3 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 3 - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 2 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 2 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 0 (;@4;) - block ;; label = @5 - block ;; label = @6 - local.get 3 - local.get 3 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 2 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 2 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@4;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 3 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 1 (;@4;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 3 - i32.load offset=16 - local.tee 2 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 2 - i32.store offset=16 - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 3 - i32.load offset=20 - local.tee 2 - i32.eqz - br_if 0 (;@4;) - local.get 6 - i32.const 20 - i32.add - local.get 2 - i32.store - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 1 (;@2;) - i32.const 0 - local.get 0 - i32.store offset=1048588 - return - end - local.get 3 - local.get 2 - i32.const -2 - i32.and - i32.store offset=4 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - end - block ;; label = @2 - local.get 0 - i32.const 255 - i32.gt_u - br_if 0 (;@2;) - local.get 0 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 2 - block ;; label = @3 - block ;; label = @4 - i32.const 0 - i32.load offset=1048580 - local.tee 4 - i32.const 1 - local.get 0 - i32.const 3 - i32.shr_u - i32.shl - local.tee 0 - i32.and - br_if 0 (;@4;) - i32.const 0 - local.get 4 - local.get 0 - i32.or - i32.store offset=1048580 - local.get 2 - local.set 0 - br 1 (;@3;) - end - local.get 2 - i32.load offset=8 - local.set 0 - end - local.get 0 - local.get 1 - i32.store offset=12 - local.get 2 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 2 - i32.store offset=12 - local.get 1 - local.get 0 - i32.store offset=8 - return - end - i32.const 31 - local.set 2 - block ;; label = @2 - local.get 0 - i32.const 16777215 - i32.gt_u - br_if 0 (;@2;) - local.get 0 - i32.const 38 - local.get 0 - i32.const 8 - i32.shr_u - i32.clz - local.tee 2 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 2 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 2 - end - local.get 1 - local.get 2 - i32.store offset=28 - local.get 1 - i64.const 0 - i64.store offset=16 align=4 - local.get 2 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 4 - block ;; label = @2 - block ;; label = @3 - i32.const 0 - i32.load offset=1048584 - local.tee 6 - i32.const 1 - local.get 2 - i32.shl - local.tee 3 - i32.and - br_if 0 (;@3;) - local.get 4 - local.get 1 - i32.store - i32.const 0 - local.get 6 - local.get 3 - i32.or - i32.store offset=1048584 - local.get 1 - local.get 4 - i32.store offset=24 - local.get 1 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 1 - i32.store offset=12 - br 1 (;@2;) - end - local.get 0 - i32.const 0 - i32.const 25 - local.get 2 - i32.const 1 - i32.shr_u - i32.sub - local.get 2 - i32.const 31 - i32.eq - select - i32.shl - local.set 2 - local.get 4 - i32.load - local.set 6 - block ;; label = @3 - loop ;; label = @4 - local.get 6 - local.tee 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 0 - i32.eq - br_if 1 (;@3;) - local.get 2 - i32.const 29 - i32.shr_u - local.set 6 - local.get 2 - i32.const 1 - i32.shl - local.set 2 - local.get 4 - local.get 6 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 6 - br_if 0 (;@4;) - end - local.get 3 - local.get 1 - i32.store - local.get 1 - local.get 4 - i32.store offset=24 - local.get 1 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 1 - i32.store offset=8 - br 1 (;@2;) - end - local.get 4 - i32.load offset=8 - local.tee 0 - local.get 1 - i32.store offset=12 - local.get 4 - local.get 1 - i32.store offset=8 - local.get 1 - i32.const 0 - i32.store offset=24 - local.get 1 - local.get 4 - i32.store offset=12 - local.get 1 - local.get 0 - i32.store offset=8 - end - i32.const 0 - i32.const 0 - i32.load offset=1048612 - i32.const -1 - i32.add - local.tee 1 - i32.const -1 - local.get 1 - select - i32.store offset=1048612 - end - ) - (func $realloc (;16;) (type 4) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 0 - br_if 0 (;@1;) - local.get 1 - call $dlmalloc - return - end - block ;; label = @1 - local.get 1 - i32.const -64 - i32.lt_u - br_if 0 (;@1;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const 0 - return - end - i32.const 16 - local.get 1 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.set 2 - local.get 0 - i32.const -4 - i32.add - local.tee 3 - i32.load - local.tee 4 - i32.const -8 - i32.and - local.set 5 - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 4 - i32.const 3 - i32.and - br_if 0 (;@3;) - local.get 2 - i32.const 256 - i32.lt_u - br_if 1 (;@2;) - local.get 5 - local.get 2 - i32.const 4 - i32.or - i32.lt_u - br_if 1 (;@2;) - local.get 5 - local.get 2 - i32.sub - i32.const 0 - i32.load offset=1049060 - i32.const 1 - i32.shl - i32.le_u - br_if 2 (;@1;) - br 1 (;@2;) - end - local.get 0 - i32.const -8 - i32.add - local.tee 6 - local.get 5 - i32.add - local.set 7 - block ;; label = @3 - local.get 5 - local.get 2 - i32.lt_u - br_if 0 (;@3;) - local.get 5 - local.get 2 - i32.sub - local.tee 1 - i32.const 16 - i32.lt_u - br_if 2 (;@1;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 2 - local.get 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 7 - local.get 7 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 1 - call $dispose_chunk - local.get 0 - return - end - block ;; label = @3 - local.get 7 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1048592 - local.get 5 - i32.add - local.tee 5 - local.get 2 - i32.le_u - br_if 1 (;@2;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - i32.const 0 - local.get 6 - local.get 2 - i32.add - local.tee 1 - i32.store offset=1048604 - i32.const 0 - local.get 5 - local.get 2 - i32.sub - local.tee 2 - i32.store offset=1048592 - local.get 1 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - block ;; label = @3 - local.get 7 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1048588 - local.get 5 - i32.add - local.tee 5 - local.get 2 - i32.lt_u - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 5 - local.get 2 - i32.sub - local.tee 1 - i32.const 16 - i32.lt_u - br_if 0 (;@5;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 2 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 6 - local.get 5 - i32.add - local.tee 5 - local.get 1 - i32.store - local.get 5 - local.get 5 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - br 1 (;@4;) - end - local.get 3 - local.get 4 - i32.const 1 - i32.and - local.get 5 - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 5 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 1 - i32.const 0 - local.set 2 - end - i32.const 0 - local.get 2 - i32.store offset=1048600 - i32.const 0 - local.get 1 - i32.store offset=1048588 - local.get 0 - return - end - local.get 7 - i32.load offset=4 - local.tee 8 - i32.const 2 - i32.and - br_if 0 (;@2;) - local.get 8 - i32.const -8 - i32.and - local.get 5 - i32.add - local.tee 9 - local.get 2 - i32.lt_u - br_if 0 (;@2;) - local.get 9 - local.get 2 - i32.sub - local.set 10 - block ;; label = @3 - block ;; label = @4 - local.get 8 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 7 - i32.load offset=8 - local.tee 1 - local.get 8 - i32.const 3 - i32.shr_u - local.tee 11 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 8 - i32.eq - drop - block ;; label = @5 - local.get 7 - i32.load offset=12 - local.tee 5 - local.get 1 - i32.ne - br_if 0 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 11 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@3;) - end - local.get 5 - local.get 8 - i32.eq - drop - local.get 5 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 5 - i32.store offset=12 - br 1 (;@3;) - end - local.get 7 - i32.load offset=24 - local.set 12 - block ;; label = @4 - block ;; label = @5 - local.get 7 - i32.load offset=12 - local.tee 8 - local.get 7 - i32.eq - br_if 0 (;@5;) - local.get 7 - i32.load offset=8 - local.tee 1 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 8 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 8 - i32.store offset=12 - br 1 (;@4;) - end - block ;; label = @5 - local.get 7 - i32.const 20 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - local.get 7 - i32.const 16 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - i32.const 0 - local.set 8 - br 1 (;@4;) - end - loop ;; label = @5 - local.get 1 - local.set 11 - local.get 5 - local.tee 8 - i32.const 20 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - local.get 8 - i32.const 16 - i32.add - local.set 1 - local.get 8 - i32.load offset=16 - local.tee 5 - br_if 0 (;@5;) - end - local.get 11 - i32.const 0 - i32.store - end - local.get 12 - i32.eqz - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 7 - local.get 7 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 1 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 1 - local.get 8 - i32.store - local.get 8 - br_if 1 (;@4;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@3;) - end - local.get 12 - i32.const 16 - i32.const 20 - local.get 12 - i32.load offset=16 - local.get 7 - i32.eq - select - i32.add - local.get 8 - i32.store - local.get 8 - i32.eqz - br_if 1 (;@3;) - end - local.get 8 - local.get 12 - i32.store offset=24 - block ;; label = @4 - local.get 7 - i32.load offset=16 - local.tee 1 - i32.eqz - br_if 0 (;@4;) - local.get 8 - local.get 1 - i32.store offset=16 - local.get 1 - local.get 8 - i32.store offset=24 - end - local.get 7 - i32.load offset=20 - local.tee 1 - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const 20 - i32.add - local.get 1 - i32.store - local.get 1 - local.get 8 - i32.store offset=24 - end - block ;; label = @3 - local.get 10 - i32.const 15 - i32.gt_u - br_if 0 (;@3;) - local.get 3 - local.get 4 - i32.const 1 - i32.and - local.get 9 - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 9 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 1 - local.get 10 - i32.const 3 - i32.or - i32.store offset=4 - local.get 6 - local.get 9 - i32.add - local.tee 2 - local.get 2 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 10 - call $dispose_chunk - local.get 0 - return - end - block ;; label = @2 - local.get 1 - call $dlmalloc - local.tee 2 - br_if 0 (;@2;) - i32.const 0 - return - end - local.get 2 - local.get 0 - i32.const -4 - i32.const -8 - local.get 3 - i32.load - local.tee 5 - i32.const 3 - i32.and - select - local.get 5 - i32.const -8 - i32.and - i32.add - local.tee 5 - local.get 1 - local.get 5 - local.get 1 - i32.lt_u - select - call $memcpy - local.set 1 - local.get 0 - call $dlfree - local.get 1 - local.set 0 - end - local.get 0 - ) - (func $dispose_chunk (;17;) (type 8) (param i32 i32) - (local i32 i32 i32 i32 i32 i32) - local.get 0 - local.get 1 - i32.add - local.set 2 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 1 - i32.and - br_if 0 (;@2;) - local.get 3 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 0 - i32.load - local.tee 3 - local.get 1 - i32.add - local.set 1 - block ;; label = @3 - block ;; label = @4 - local.get 0 - local.get 3 - i32.sub - local.tee 0 - i32.const 0 - i32.load offset=1048600 - i32.eq - br_if 0 (;@4;) - block ;; label = @5 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 0 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - local.get 0 - i32.load offset=12 - local.tee 3 - local.get 4 - i32.ne - br_if 2 (;@3;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 3 (;@2;) - end - local.get 0 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 0 - i32.load offset=12 - local.tee 6 - local.get 0 - i32.eq - br_if 0 (;@6;) - local.get 0 - i32.load offset=8 - local.tee 3 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 0 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 0 - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 3 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 3 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 2 (;@2;) - block ;; label = @5 - block ;; label = @6 - local.get 0 - local.get 0 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 4 (;@2;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 0 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 3 (;@2;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 0 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 0 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 2 (;@2;) - local.get 6 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - br 2 (;@2;) - end - local.get 2 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 1 (;@2;) - local.get 2 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - i32.const 0 - local.get 1 - i32.store offset=1048588 - local.get 2 - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - return - end - local.get 3 - local.get 6 - i32.eq - drop - local.get 3 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 3 - i32.store offset=12 - end - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.load offset=4 - local.tee 3 - i32.const 2 - i32.and - br_if 0 (;@3;) - block ;; label = @4 - local.get 2 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 0 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 1 - i32.add - local.tee 1 - i32.store offset=1048592 - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 3 (;@1;) - i32.const 0 - i32.const 0 - i32.store offset=1048588 - i32.const 0 - i32.const 0 - i32.store offset=1048600 - return - end - block ;; label = @4 - local.get 2 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 0 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 1 - i32.add - local.tee 1 - i32.store offset=1048588 - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - return - end - local.get 3 - i32.const -8 - i32.and - local.get 1 - i32.add - local.set 1 - block ;; label = @4 - block ;; label = @5 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 2 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @6 - local.get 2 - i32.load offset=12 - local.tee 3 - local.get 4 - i32.ne - br_if 0 (;@6;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@4;) - end - local.get 3 - local.get 6 - i32.eq - drop - local.get 3 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 3 - i32.store offset=12 - br 1 (;@4;) - end - local.get 2 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 2 - i32.load offset=12 - local.tee 6 - local.get 2 - i32.eq - br_if 0 (;@6;) - local.get 2 - i32.load offset=8 - local.tee 3 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 2 - i32.const 20 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - local.get 2 - i32.const 16 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 4 - local.set 5 - local.get 3 - local.tee 6 - i32.const 20 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 4 - local.get 6 - i32.load offset=16 - local.tee 3 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 0 (;@4;) - block ;; label = @5 - block ;; label = @6 - local.get 2 - local.get 2 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@4;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 2 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 1 (;@4;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 2 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 2 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 0 (;@4;) - local.get 6 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 1 (;@2;) - i32.const 0 - local.get 1 - i32.store offset=1048588 - return - end - local.get 2 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - end - block ;; label = @2 - local.get 1 - i32.const 255 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 3 - block ;; label = @3 - block ;; label = @4 - i32.const 0 - i32.load offset=1048580 - local.tee 4 - i32.const 1 - local.get 1 - i32.const 3 - i32.shr_u - i32.shl - local.tee 1 - i32.and - br_if 0 (;@4;) - i32.const 0 - local.get 4 - local.get 1 - i32.or - i32.store offset=1048580 - local.get 3 - local.set 1 - br 1 (;@3;) - end - local.get 3 - i32.load offset=8 - local.set 1 - end - local.get 1 - local.get 0 - i32.store offset=12 - local.get 3 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 3 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - return - end - i32.const 31 - local.set 3 - block ;; label = @2 - local.get 1 - i32.const 16777215 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.const 38 - local.get 1 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 0 - local.get 3 - i32.store offset=28 - local.get 0 - i64.const 0 - i64.store offset=16 align=4 - local.get 3 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 4 - block ;; label = @2 - i32.const 0 - i32.load offset=1048584 - local.tee 6 - i32.const 1 - local.get 3 - i32.shl - local.tee 2 - i32.and - br_if 0 (;@2;) - local.get 4 - local.get 0 - i32.store - i32.const 0 - local.get 6 - local.get 2 - i32.or - i32.store offset=1048584 - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 0 - i32.store offset=12 - return - end - local.get 1 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 4 - i32.load - local.set 6 - block ;; label = @2 - loop ;; label = @3 - local.get 6 - local.tee 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 1 - i32.eq - br_if 1 (;@2;) - local.get 3 - i32.const 29 - i32.shr_u - local.set 6 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 4 - local.get 6 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 6 - br_if 0 (;@3;) - end - local.get 2 - local.get 0 - i32.store - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=12 - local.get 0 - local.get 0 - i32.store offset=8 - return - end - local.get 4 - i32.load offset=8 - local.tee 1 - local.get 0 - i32.store offset=12 - local.get 4 - local.get 0 - i32.store offset=8 - local.get 0 - i32.const 0 - i32.store offset=24 - local.get 0 - local.get 4 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - end - ) - (func $internal_memalign (;18;) (type 4) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.const 16 - local.get 0 - i32.const 16 - i32.gt_u - select - local.tee 2 - local.get 2 - i32.const -1 - i32.add - i32.and - br_if 0 (;@2;) - local.get 2 - local.set 0 - br 1 (;@1;) - end - i32.const 32 - local.set 3 - loop ;; label = @2 - local.get 3 - local.tee 0 - i32.const 1 - i32.shl - local.set 3 - local.get 0 - local.get 2 - i32.lt_u - br_if 0 (;@2;) - end - end - block ;; label = @1 - i32.const -64 - local.get 0 - i32.sub - local.get 1 - i32.gt_u - br_if 0 (;@1;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const 0 - return - end - block ;; label = @1 - local.get 0 - i32.const 16 - local.get 1 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.tee 1 - i32.add - i32.const 12 - i32.add - call $dlmalloc - local.tee 3 - br_if 0 (;@1;) - i32.const 0 - return - end - local.get 3 - i32.const -8 - i32.add - local.set 2 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.const -1 - i32.add - local.get 3 - i32.and - br_if 0 (;@2;) - local.get 2 - local.set 0 - br 1 (;@1;) - end - local.get 3 - i32.const -4 - i32.add - local.tee 4 - i32.load - local.tee 5 - i32.const -8 - i32.and - local.get 3 - local.get 0 - i32.add - i32.const -1 - i32.add - i32.const 0 - local.get 0 - i32.sub - i32.and - i32.const -8 - i32.add - local.tee 3 - i32.const 0 - local.get 0 - local.get 3 - local.get 2 - i32.sub - i32.const 15 - i32.gt_u - select - i32.add - local.tee 0 - local.get 2 - i32.sub - local.tee 3 - i32.sub - local.set 6 - block ;; label = @2 - local.get 5 - i32.const 3 - i32.and - br_if 0 (;@2;) - local.get 0 - local.get 6 - i32.store offset=4 - local.get 0 - local.get 2 - i32.load - local.get 3 - i32.add - i32.store - br 1 (;@1;) - end - local.get 0 - local.get 6 - local.get 0 - i32.load offset=4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 0 - local.get 6 - i32.add - local.tee 6 - local.get 6 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - local.get 4 - i32.load - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 2 - local.get 3 - i32.add - local.tee 6 - local.get 6 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 3 - call $dispose_chunk - end - block ;; label = @1 - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@1;) - local.get 3 - i32.const -8 - i32.and - local.tee 2 - local.get 1 - i32.const 16 - i32.add - i32.le_u - br_if 0 (;@1;) - local.get 0 - local.get 1 - local.get 3 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.tee 3 - local.get 2 - local.get 1 - i32.sub - local.tee 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 2 - i32.add - local.tee 2 - local.get 2 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 1 - call $dispose_chunk - end - local.get 0 - i32.const 8 - i32.add - ) - (func $aligned_alloc (;19;) (type 4) (param i32 i32) (result i32) - block ;; label = @1 - local.get 0 - i32.const 16 - i32.gt_u - br_if 0 (;@1;) - local.get 1 - call $dlmalloc - return - end - local.get 0 - local.get 1 - call $internal_memalign - ) - (func $abort (;20;) (type 2) - unreachable - unreachable - ) - (func $sbrk (;21;) (type 6) (param i32) (result i32) - block ;; label = @1 - local.get 0 - br_if 0 (;@1;) - memory.size - i32.const 16 - i32.shl - return - end - block ;; label = @1 - local.get 0 - i32.const 65535 - i32.and - br_if 0 (;@1;) - local.get 0 - i32.const -1 - i32.le_s - br_if 0 (;@1;) - block ;; label = @2 - local.get 0 - i32.const 16 - i32.shr_u - memory.grow - local.tee 0 - i32.const -1 - i32.ne - br_if 0 (;@2;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const -1 - return - end - local.get 0 - i32.const 16 - i32.shl - return - end - call $abort - unreachable - ) - (func $memcpy (;22;) (type 9) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 32 - i32.gt_u - br_if 0 (;@3;) - local.get 1 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@2;) - local.get 2 - i32.eqz - br_if 1 (;@2;) - local.get 0 - local.get 1 - i32.load8_u - i32.store8 - local.get 2 - i32.const -1 - i32.add - local.set 3 - local.get 0 - i32.const 1 - i32.add - local.set 4 - local.get 1 - i32.const 1 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=1 - i32.store8 offset=1 - local.get 2 - i32.const -2 - i32.add - local.set 3 - local.get 0 - i32.const 2 - i32.add - local.set 4 - local.get 1 - i32.const 2 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=2 - i32.store8 offset=2 - local.get 2 - i32.const -3 - i32.add - local.set 3 - local.get 0 - i32.const 3 - i32.add - local.set 4 - local.get 1 - i32.const 3 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=3 - i32.store8 offset=3 - local.get 2 - i32.const -4 - i32.add - local.set 3 - local.get 0 - i32.const 4 - i32.add - local.set 4 - local.get 1 - i32.const 4 - i32.add - local.set 5 - br 2 (;@1;) - end - local.get 0 - local.get 1 - local.get 2 - memory.copy - local.get 0 - return - end - local.get 2 - local.set 3 - local.get 0 - local.set 4 - local.get 1 - local.set 5 - end - block ;; label = @1 - block ;; label = @2 - local.get 4 - i32.const 3 - i32.and - local.tee 2 - br_if 0 (;@2;) - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 16 - i32.ge_u - br_if 0 (;@4;) - local.get 3 - local.set 2 - br 1 (;@3;) - end - block ;; label = @4 - local.get 3 - i32.const -16 - i32.add - local.tee 2 - i32.const 16 - i32.and - br_if 0 (;@4;) - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 4 - local.get 5 - i64.load offset=8 align=4 - i64.store offset=8 align=4 - local.get 4 - i32.const 16 - i32.add - local.set 4 - local.get 5 - i32.const 16 - i32.add - local.set 5 - local.get 2 - local.set 3 - end - local.get 2 - i32.const 16 - i32.lt_u - br_if 0 (;@3;) - local.get 3 - local.set 2 - loop ;; label = @4 - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 4 - local.get 5 - i64.load offset=8 align=4 - i64.store offset=8 align=4 - local.get 4 - local.get 5 - i64.load offset=16 align=4 - i64.store offset=16 align=4 - local.get 4 - local.get 5 - i64.load offset=24 align=4 - i64.store offset=24 align=4 - local.get 4 - i32.const 32 - i32.add - local.set 4 - local.get 5 - i32.const 32 - i32.add - local.set 5 - local.get 2 - i32.const -32 - i32.add - local.tee 2 - i32.const 15 - i32.gt_u - br_if 0 (;@4;) - end - end - block ;; label = @3 - local.get 2 - i32.const 8 - i32.lt_u - br_if 0 (;@3;) - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 5 - i32.const 8 - i32.add - local.set 5 - local.get 4 - i32.const 8 - i32.add - local.set 4 - end - block ;; label = @3 - local.get 2 - i32.const 4 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 4 - local.get 5 - i32.load - i32.store - local.get 5 - i32.const 4 - i32.add - local.set 5 - local.get 4 - i32.const 4 - i32.add - local.set 4 - end - block ;; label = @3 - local.get 2 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 4 - local.get 5 - i32.load16_u align=1 - i32.store16 align=1 - local.get 4 - i32.const 2 - i32.add - local.set 4 - local.get 5 - i32.const 2 - i32.add - local.set 5 - end - local.get 2 - i32.const 1 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 4 - local.get 5 - i32.load8_u - i32.store8 - local.get 0 - return - end - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.const 32 - i32.lt_u - br_if 0 (;@6;) - block ;; label = @7 - block ;; label = @8 - local.get 2 - i32.const -1 - i32.add - br_table 3 (;@5;) 0 (;@8;) 1 (;@7;) 7 (;@1;) - end - local.get 4 - local.get 5 - i32.load - i32.store16 align=1 - local.get 4 - local.get 5 - i32.const 2 - i32.add - i32.load align=2 - i32.store offset=2 - local.get 4 - local.get 5 - i32.const 6 - i32.add - i64.load align=2 - i64.store offset=6 align=4 - local.get 4 - i32.const 18 - i32.add - local.set 2 - local.get 5 - i32.const 18 - i32.add - local.set 1 - i32.const 14 - local.set 6 - local.get 5 - i32.const 14 - i32.add - i32.load align=2 - local.set 5 - i32.const 14 - local.set 3 - br 3 (;@4;) - end - local.get 4 - local.get 5 - i32.load - i32.store8 - local.get 4 - local.get 5 - i32.const 1 - i32.add - i32.load align=1 - i32.store offset=1 - local.get 4 - local.get 5 - i32.const 5 - i32.add - i64.load align=1 - i64.store offset=5 align=4 - local.get 4 - i32.const 17 - i32.add - local.set 2 - local.get 5 - i32.const 17 - i32.add - local.set 1 - i32.const 13 - local.set 6 - local.get 5 - i32.const 13 - i32.add - i32.load align=1 - local.set 5 - i32.const 15 - local.set 3 - br 2 (;@4;) - end - block ;; label = @6 - block ;; label = @7 - local.get 3 - i32.const 16 - i32.ge_u - br_if 0 (;@7;) - local.get 4 - local.set 2 - local.get 5 - local.set 1 - br 1 (;@6;) - end - local.get 4 - local.get 5 - i32.load8_u - i32.store8 - local.get 4 - local.get 5 - i32.load offset=1 align=1 - i32.store offset=1 align=1 - local.get 4 - local.get 5 - i64.load offset=5 align=1 - i64.store offset=5 align=1 - local.get 4 - local.get 5 - i32.load16_u offset=13 align=1 - i32.store16 offset=13 align=1 - local.get 4 - local.get 5 - i32.load8_u offset=15 - i32.store8 offset=15 - local.get 4 - i32.const 16 - i32.add - local.set 2 - local.get 5 - i32.const 16 - i32.add - local.set 1 - end - local.get 3 - i32.const 8 - i32.and - br_if 2 (;@3;) - br 3 (;@2;) - end - local.get 4 - local.get 5 - i32.load - local.tee 2 - i32.store8 - local.get 4 - local.get 2 - i32.const 16 - i32.shr_u - i32.store8 offset=2 - local.get 4 - local.get 2 - i32.const 8 - i32.shr_u - i32.store8 offset=1 - local.get 4 - local.get 5 - i32.const 3 - i32.add - i32.load align=1 - i32.store offset=3 - local.get 4 - local.get 5 - i32.const 7 - i32.add - i64.load align=1 - i64.store offset=7 align=4 - local.get 4 - i32.const 19 - i32.add - local.set 2 - local.get 5 - i32.const 19 - i32.add - local.set 1 - i32.const 15 - local.set 6 - local.get 5 - i32.const 15 - i32.add - i32.load align=1 - local.set 5 - i32.const 13 - local.set 3 - end - local.get 4 - local.get 6 - i32.add - local.get 5 - i32.store - end - local.get 2 - local.get 1 - i64.load align=1 - i64.store align=1 - local.get 2 - i32.const 8 - i32.add - local.set 2 - local.get 1 - i32.const 8 - i32.add - local.set 1 - end - block ;; label = @2 - local.get 3 - i32.const 4 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - local.get 1 - i32.load align=1 - i32.store align=1 - local.get 2 - i32.const 4 - i32.add - local.set 2 - local.get 1 - i32.const 4 - i32.add - local.set 1 - end - block ;; label = @2 - local.get 3 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - local.get 1 - i32.load16_u align=1 - i32.store16 align=1 - local.get 2 - i32.const 2 - i32.add - local.set 2 - local.get 1 - i32.const 2 - i32.add - local.set 1 - end - local.get 3 - i32.const 1 - i32.and - i32.eqz - br_if 0 (;@1;) - local.get 2 - local.get 1 - i32.load8_u - i32.store8 - end - local.get 0 - ) - (table (;0;) 1 1 funcref) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (export "memory" (memory 0)) - (export "miden:basic-wallet/basic-wallet@1.0.0#receive-asset" (func $miden:basic-wallet/basic-wallet@1.0.0#receive-asset)) - (export "miden:basic-wallet/basic-wallet@1.0.0#send-asset" (func $miden:basic-wallet/basic-wallet@1.0.0#send-asset)) - (export "cabi_realloc" (func $cabi_realloc)) - ) - (core module (;1;) - (type (;0;) (func (param i32 i64 i64 i64 i64 i32))) - (func $indirect-miden:base/tx-kernel@1.0.0-add-asset (;0;) (type 0) (param i32 i64 i64 i64 i64 i32) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - local.get 4 - local.get 5 - i32.const 0 - call_indirect (type 0) - ) - (func $indirect-miden:base/tx-kernel@1.0.0-remove-asset (;1;) (type 0) (param i32 i64 i64 i64 i64 i32) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - local.get 4 - local.get 5 - i32.const 1 - call_indirect (type 0) - ) - (table (;0;) 2 2 funcref) - (export "0" (func $indirect-miden:base/tx-kernel@1.0.0-add-asset)) - (export "1" (func $indirect-miden:base/tx-kernel@1.0.0-remove-asset)) - (export "$imports" (table 0)) - ) - (core module (;2;) - (type (;0;) (func (param i32 i64 i64 i64 i64 i32))) - (import "" "0" (func (;0;) (type 0))) - (import "" "1" (func (;1;) (type 0))) - (import "" "$imports" (table (;0;) 2 2 funcref)) - (elem (;0;) (i32.const 0) func 0 1) - ) - (core instance (;0;) (instantiate 1)) - (alias core export 0 "0" (core func (;0;))) - (alias core export 0 "1" (core func (;1;))) - (alias export 1 "create-note" (func (;0;))) - (core func (;2;) (canon lower (func 0))) - (core instance (;1;) - (export "add-asset" (func 0)) - (export "remove-asset" (func 1)) - (export "create-note" (func 2)) - ) - (core instance (;2;) (instantiate 0 - (with "miden:base/tx-kernel@1.0.0" (instance 1)) - ) - ) - (alias core export 2 "memory" (core memory (;0;))) - (alias core export 2 "cabi_realloc" (core func (;3;))) - (alias core export 0 "$imports" (core table (;0;))) - (alias export 1 "add-asset" (func (;1;))) - (core func (;4;) (canon lower (func 1) (memory 0))) - (alias export 1 "remove-asset" (func (;2;))) - (core func (;5;) (canon lower (func 2) (memory 0))) - (core instance (;3;) - (export "$imports" (table 0)) - (export "0" (func 4)) - (export "1" (func 5)) - ) - (core instance (;4;) (instantiate 2 - (with "" (instance 3)) - ) - ) - (component (;0;)) - (instance (;2;) (instantiate 0)) - (export (;3;) "miden:base/account@1.0.0" (instance 2)) - (alias export 0 "asset" (type (;5;))) - (type (;6;) (func (param "asset" 5))) - (alias core export 2 "miden:basic-wallet/basic-wallet@1.0.0#receive-asset" (core func (;6;))) - (func (;3;) (type 6) (canon lift (core func 6))) - (alias export 0 "tag" (type (;7;))) - (alias export 0 "recipient" (type (;8;))) - (type (;9;) (func (param "asset" 5) (param "tag" 7) (param "recipient" 8))) - (alias core export 2 "miden:basic-wallet/basic-wallet@1.0.0#send-asset" (core func (;7;))) - (func (;4;) (type 9) (canon lift (core func 7))) - (alias export 0 "felt" (type (;10;))) - (alias export 0 "account-id" (type (;11;))) - (alias export 0 "fungible-asset" (type (;12;))) - (alias export 0 "word" (type (;13;))) - (alias export 0 "non-fungible-asset" (type (;14;))) - (alias export 0 "asset" (type (;15;))) - (alias export 0 "tag" (type (;16;))) - (alias export 0 "recipient" (type (;17;))) - (component (;1;) - (type (;0;) u64) - (import "import-type-felt" (type (;1;) (eq 0))) - (import "import-type-account-id" (type (;2;) (eq 1))) - (type (;3;) (record (field "asset" 2) (field "amount" u64))) - (import "import-type-fungible-asset" (type (;4;) (eq 3))) - (type (;5;) (tuple 1 1 1 1)) - (import "import-type-word" (type (;6;) (eq 5))) - (import "import-type-non-fungible-asset" (type (;7;) (eq 6))) - (type (;8;) (variant (case "fungible" 4) (case "non-fungible" 7))) - (import "import-type-asset" (type (;9;) (eq 8))) - (import "import-type-tag" (type (;10;) (eq 1))) - (import "import-type-recipient" (type (;11;) (eq 6))) - (import "import-type-asset0" (type (;12;) (eq 9))) - (type (;13;) (func (param "asset" 12))) - (import "import-func-receive-asset" (func (;0;) (type 13))) - (import "import-type-tag0" (type (;14;) (eq 10))) - (import "import-type-recipient0" (type (;15;) (eq 11))) - (type (;16;) (func (param "asset" 12) (param "tag" 14) (param "recipient" 15))) - (import "import-func-send-asset" (func (;1;) (type 16))) - (export (;17;) "asset" (type 9)) - (export (;18;) "tag" (type 10)) - (export (;19;) "recipient" (type 11)) - (type (;20;) (func (param "asset" 17))) - (export (;2;) "receive-asset" (func 0) (func (type 20))) - (type (;21;) (func (param "asset" 17) (param "tag" 18) (param "recipient" 19))) - (export (;3;) "send-asset" (func 1) (func (type 21))) - ) - (instance (;4;) (instantiate 1 - (with "import-func-receive-asset" (func 3)) - (with "import-func-send-asset" (func 4)) - (with "import-type-felt" (type 10)) - (with "import-type-account-id" (type 11)) - (with "import-type-fungible-asset" (type 12)) - (with "import-type-word" (type 13)) - (with "import-type-non-fungible-asset" (type 14)) - (with "import-type-asset" (type 15)) - (with "import-type-tag" (type 16)) - (with "import-type-recipient" (type 17)) - (with "import-type-asset0" (type 5)) - (with "import-type-tag0" (type 7)) - (with "import-type-recipient0" (type 8)) - ) - ) - (export (;5;) "miden:basic-wallet/basic-wallet@1.0.0" (instance 4)) -) \ No newline at end of file diff --git a/tests/integration/expected/sdk_basic_wallet/basic_wallet_p2id_note.wat b/tests/integration/expected/sdk_basic_wallet/basic_wallet_p2id_note.wat deleted file mode 100644 index 58be1f60f..000000000 --- a/tests/integration/expected/sdk_basic_wallet/basic_wallet_p2id_note.wat +++ /dev/null @@ -1,6748 +0,0 @@ -(component - (type (;0;) - (instance - (type (;0;) u64) - (export (;1;) "felt" (type (eq 0))) - (export (;2;) "account-id" (type (eq 1))) - (type (;3;) (list 1)) - (export (;4;) "note-inputs" (type (eq 3))) - (type (;5;) (record (field "asset" 2) (field "amount" u64))) - (export (;6;) "fungible-asset" (type (eq 5))) - (type (;7;) (tuple 1 1 1 1)) - (export (;8;) "word" (type (eq 7))) - (export (;9;) "non-fungible-asset" (type (eq 8))) - (type (;10;) (variant (case "fungible" 6) (case "non-fungible" 9))) - (export (;11;) "asset" (type (eq 10))) - ) - ) - (import "miden:base/types@1.0.0" (instance (;0;) (type 0))) - (alias export 0 "account-id" (type (;1;))) - (alias export 0 "note-inputs" (type (;2;))) - (alias export 0 "asset" (type (;3;))) - (type (;4;) - (instance - (alias outer 1 1 (type (;0;))) - (export (;1;) "account-id" (type (eq 0))) - (alias outer 1 2 (type (;2;))) - (export (;3;) "note-inputs" (type (eq 2))) - (alias outer 1 3 (type (;4;))) - (export (;5;) "asset" (type (eq 4))) - (type (;6;) (func (result 1))) - (export (;0;) "get-id" (func (type 6))) - (type (;7;) (func (result 3))) - (export (;1;) "get-inputs" (func (type 7))) - (type (;8;) (list 5)) - (type (;9;) (func (result 8))) - (export (;2;) "get-assets" (func (type 9))) - ) - ) - (import "miden:base/tx-kernel@1.0.0" (instance (;1;) (type 4))) - (alias export 0 "asset" (type (;5;))) - (type (;6;) - (instance - (alias outer 1 5 (type (;0;))) - (export (;1;) "asset" (type (eq 0))) - (type (;2;) (func (param "asset" 1))) - (export (;0;) "receive-asset" (func (type 2))) - ) - ) - (import "miden:basic-wallet/basic-wallet@1.0.0" (instance (;2;) (type 6))) - (core module (;0;) - (type (;0;) (func (param i32))) - (type (;1;) (func (result i64))) - (type (;2;) (func (param i32 i64 i64 i64 i64))) - (type (;3;) (func)) - (type (;4;) (func (param i32 i32 i32 i32))) - (type (;5;) (func (param i32 i32))) - (type (;6;) (func (param i32 i32) (result i32))) - (type (;7;) (func (param i32 i32 i32))) - (type (;8;) (func (param i32 i32 i32 i32) (result i32))) - (type (;9;) (func (param i32) (result i32))) - (type (;10;) (func (param i32 i32 i32) (result i32))) - (import "miden:base/tx-kernel@1.0.0" "get-inputs" (func $basic_wallet_p2id_note::bindings::miden::base::tx_kernel::get_inputs::wit_import (;0;) (type 0))) - (import "miden:base/tx-kernel@1.0.0" "get-id" (func $basic_wallet_p2id_note::bindings::miden::base::tx_kernel::get_id::wit_import (;1;) (type 1))) - (import "miden:base/tx-kernel@1.0.0" "get-assets" (func $basic_wallet_p2id_note::bindings::miden::base::tx_kernel::get_assets::wit_import (;2;) (type 0))) - (import "miden:basic-wallet/basic-wallet@1.0.0" "receive-asset" (func $basic_wallet_p2id_note::bindings::miden::basic_wallet::basic_wallet::receive_asset::wit_import (;3;) (type 2))) - (func $__wasm_call_ctors (;4;) (type 3)) - (func $alloc::raw_vec::finish_grow (;5;) (type 4) (param i32 i32 i32 i32) - (local i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 1 - i32.eqz - br_if 0 (;@3;) - local.get 2 - i32.const -1 - i32.le_s - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.load offset=4 - i32.eqz - br_if 0 (;@6;) - block ;; label = @7 - local.get 3 - i32.const 8 - i32.add - i32.load - local.tee 4 - br_if 0 (;@7;) - block ;; label = @8 - local.get 2 - br_if 0 (;@8;) - local.get 1 - local.set 3 - br 4 (;@4;) - end - i32.const 0 - i32.load8_u offset=1048576 - drop - br 2 (;@5;) - end - local.get 3 - i32.load - local.get 4 - local.get 1 - local.get 2 - call $__rust_realloc - local.set 3 - br 2 (;@4;) - end - block ;; label = @6 - local.get 2 - br_if 0 (;@6;) - local.get 1 - local.set 3 - br 2 (;@4;) - end - i32.const 0 - i32.load8_u offset=1048576 - drop - end - local.get 2 - local.get 1 - call $__rust_alloc - local.set 3 - end - block ;; label = @4 - local.get 3 - i32.eqz - br_if 0 (;@4;) - local.get 0 - local.get 3 - i32.store offset=4 - local.get 0 - i32.const 8 - i32.add - local.get 2 - i32.store - local.get 0 - i32.const 0 - i32.store - return - end - local.get 0 - local.get 1 - i32.store offset=4 - local.get 0 - i32.const 8 - i32.add - local.get 2 - i32.store - br 2 (;@1;) - end - local.get 0 - i32.const 0 - i32.store offset=4 - local.get 0 - i32.const 8 - i32.add - local.get 2 - i32.store - br 1 (;@1;) - end - local.get 0 - i32.const 0 - i32.store offset=4 - end - local.get 0 - i32.const 1 - i32.store - ) - (func $alloc::raw_vec::RawVec::reserve_for_push (;6;) (type 5) (param i32 i32) - (local i32 i32 i32 i32) - global.get $__stack_pointer - i32.const 32 - i32.sub - local.tee 2 - global.set $__stack_pointer - block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.const 1 - i32.add - local.tee 1 - i32.eqz - br_if 0 (;@2;) - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 1 - i32.shl - local.tee 4 - local.get 1 - local.get 4 - local.get 1 - i32.gt_u - select - local.tee 1 - i32.const 4 - local.get 1 - i32.const 4 - i32.gt_u - select - local.tee 1 - i32.const 40 - i32.mul - local.set 4 - local.get 1 - i32.const 53687092 - i32.lt_u - i32.const 3 - i32.shl - local.set 5 - block ;; label = @3 - block ;; label = @4 - local.get 3 - br_if 0 (;@4;) - local.get 2 - i32.const 0 - i32.store offset=24 - br 1 (;@3;) - end - local.get 2 - i32.const 8 - i32.store offset=24 - local.get 2 - local.get 3 - i32.const 40 - i32.mul - i32.store offset=28 - local.get 2 - local.get 0 - i32.load - i32.store offset=20 - end - local.get 2 - i32.const 8 - i32.add - local.get 5 - local.get 4 - local.get 2 - i32.const 20 - i32.add - call $alloc::raw_vec::finish_grow - local.get 2 - i32.load offset=12 - local.set 3 - block ;; label = @3 - local.get 2 - i32.load offset=8 - br_if 0 (;@3;) - local.get 0 - local.get 1 - i32.store offset=4 - local.get 0 - local.get 3 - i32.store - br 2 (;@1;) - end - local.get 3 - i32.const -2147483647 - i32.eq - br_if 1 (;@1;) - local.get 3 - i32.eqz - br_if 0 (;@2;) - local.get 3 - local.get 2 - i32.const 16 - i32.add - i32.load - call $alloc::alloc::handle_alloc_error - unreachable - end - call $alloc::raw_vec::capacity_overflow - unreachable - end - local.get 2 - i32.const 32 - i32.add - global.set $__stack_pointer - ) - (func $miden:base/note@1.0.0#note-script (;7;) (type 3) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i64 i64 i64 i64 i64 i32) - global.get $__stack_pointer - i32.const 32 - i32.sub - local.tee 0 - global.set $__stack_pointer - call $wit_bindgen::rt::run_ctors_once - local.get 0 - i32.const 20 - i32.add - call $basic_wallet_p2id_note::bindings::miden::base::tx_kernel::get_inputs::wit_import - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - local.get 0 - i32.const 24 - i32.add - i32.load - local.tee 1 - i32.eqz - br_if 0 (;@5;) - local.get 0 - i32.load offset=20 - local.tee 2 - i64.load - call $basic_wallet_p2id_note::bindings::miden::base::tx_kernel::get_id::wit_import - i64.ne - br_if 0 (;@5;) - local.get 0 - i32.const 12 - i32.add - call $basic_wallet_p2id_note::bindings::miden::base::tx_kernel::get_assets::wit_import - local.get 0 - i32.load offset=12 - local.set 3 - block ;; label = @6 - local.get 0 - i32.const 16 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@6;) - local.get 4 - i32.const 53687091 - i32.gt_u - br_if 2 (;@4;) - local.get 4 - i32.const 40 - i32.mul - local.tee 5 - i32.const -1 - i32.le_s - br_if 2 (;@4;) - i32.const 0 - local.set 6 - i32.const 0 - i32.load8_u offset=1048576 - drop - local.get 5 - i32.const 8 - call $__rust_alloc - local.tee 7 - i32.eqz - br_if 3 (;@3;) - local.get 0 - i32.const 0 - i32.store offset=28 - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 7 - i32.store offset=20 - local.get 3 - local.set 8 - loop ;; label = @7 - block ;; label = @8 - block ;; label = @9 - local.get 8 - i32.load8_u - br_if 0 (;@9;) - i64.const 0 - local.set 9 - br 1 (;@8;) - end - local.get 8 - i32.const 32 - i32.add - i64.load - local.set 10 - local.get 8 - i32.const 24 - i32.add - i64.load - local.set 11 - i64.const 1 - local.set 9 - end - local.get 8 - i32.const 8 - i32.add - i64.load - local.set 12 - local.get 8 - i32.const 16 - i32.add - i64.load - local.set 13 - block ;; label = @8 - local.get 6 - local.get 0 - i32.load offset=24 - i32.ne - br_if 0 (;@8;) - local.get 0 - i32.const 20 - i32.add - local.get 6 - call $alloc::raw_vec::RawVec::reserve_for_push - local.get 0 - i32.load offset=20 - local.set 7 - local.get 0 - i32.load offset=28 - local.set 6 - end - local.get 7 - local.get 6 - i32.const 40 - i32.mul - i32.add - local.tee 14 - local.get 10 - i64.store offset=32 - local.get 14 - local.get 11 - i64.store offset=24 - local.get 14 - local.get 13 - i64.store offset=16 - local.get 14 - local.get 12 - i64.store offset=8 - local.get 14 - local.get 9 - i64.store - local.get 0 - local.get 6 - i32.const 1 - i32.add - local.tee 6 - i32.store offset=28 - local.get 8 - i32.const 40 - i32.add - local.set 8 - local.get 4 - i32.const -1 - i32.add - local.tee 4 - br_if 0 (;@7;) - end - local.get 0 - i32.load offset=24 - local.set 7 - local.get 0 - i32.load offset=20 - local.set 4 - local.get 3 - local.get 5 - i32.const 8 - call $wit_bindgen::rt::dealloc - local.get 6 - i32.eqz - br_if 4 (;@2;) - local.get 4 - local.get 6 - i32.const 40 - i32.mul - i32.add - local.set 14 - local.get 4 - local.set 8 - loop ;; label = @7 - local.get 8 - i64.load - local.tee 9 - i64.const 2 - i64.eq - br_if 5 (;@2;) - local.get 9 - i64.const 0 - i64.ne - local.tee 6 - local.get 8 - i64.load offset=8 - local.get 8 - i64.load offset=16 - local.get 8 - i64.load offset=24 - i64.const 0 - local.get 6 - select - local.get 8 - i64.load offset=32 - i64.const 0 - local.get 6 - select - call $basic_wallet_p2id_note::bindings::miden::basic_wallet::basic_wallet::receive_asset::wit_import - local.get 8 - i32.const 40 - i32.add - local.tee 8 - local.get 14 - i32.ne - br_if 0 (;@7;) - br 5 (;@2;) - end - end - local.get 3 - i32.const 0 - i32.const 8 - call $wit_bindgen::rt::dealloc - br 4 (;@1;) - end - unreachable - unreachable - end - call $alloc::raw_vec::capacity_overflow - unreachable - end - i32.const 8 - local.get 5 - call $alloc::alloc::handle_alloc_error - unreachable - end - local.get 7 - i32.eqz - br_if 0 (;@1;) - local.get 4 - local.get 7 - i32.const 40 - i32.mul - i32.const 8 - call $__rust_dealloc - end - local.get 2 - local.get 1 - i32.const 3 - i32.shl - i32.const 8 - call $__rust_dealloc - local.get 0 - i32.const 32 - i32.add - global.set $__stack_pointer - ) - (func $__rust_alloc (;8;) (type 6) (param i32 i32) (result i32) - (local i32) - local.get 0 - local.get 1 - call $__rdl_alloc - local.set 2 - local.get 2 - return - ) - (func $__rust_dealloc (;9;) (type 7) (param i32 i32 i32) - local.get 0 - local.get 1 - local.get 2 - call $__rdl_dealloc - return - ) - (func $__rust_realloc (;10;) (type 8) (param i32 i32 i32 i32) (result i32) - (local i32) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - call $__rdl_realloc - local.set 4 - local.get 4 - return - ) - (func $wit_bindgen::rt::run_ctors_once (;11;) (type 3) - block ;; label = @1 - i32.const 0 - i32.load8_u offset=1048577 - br_if 0 (;@1;) - call $__wasm_call_ctors - i32.const 0 - i32.const 1 - i32.store8 offset=1048577 - end - ) - (func $cabi_realloc (;12;) (type 8) (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=1048576 - 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 - local.get 3 - call $__rust_realloc - local.set 2 - end - local.get 2 - br_if 0 (;@1;) - unreachable - unreachable - end - local.get 2 - ) - (func $wit_bindgen::rt::dealloc (;13;) (type 7) (param i32 i32 i32) - block ;; label = @1 - local.get 1 - i32.eqz - br_if 0 (;@1;) - local.get 0 - local.get 1 - local.get 2 - call $__rust_dealloc - end - ) - (func $__rdl_alloc (;14;) (type 6) (param i32 i32) (result i32) - (local i32) - block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.const 8 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - local.get 0 - i32.le_u - br_if 1 (;@1;) - end - local.get 1 - local.get 1 - local.get 0 - local.get 1 - i32.rem_u - local.tee 2 - i32.sub - i32.const 0 - local.get 2 - select - local.get 0 - i32.add - call $aligned_alloc - return - end - local.get 0 - call $malloc - ) - (func $__rdl_dealloc (;15;) (type 7) (param i32 i32 i32) - local.get 0 - call $free - ) - (func $__rdl_realloc (;16;) (type 8) (param i32 i32 i32 i32) (result i32) - (local i32 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 8 - i32.gt_u - br_if 0 (;@3;) - local.get 2 - local.get 3 - i32.le_u - br_if 1 (;@2;) - end - i32.const 0 - local.set 4 - local.get 2 - local.get 2 - local.get 3 - local.get 2 - i32.rem_u - local.tee 5 - i32.sub - i32.const 0 - local.get 5 - select - local.get 3 - i32.add - call $aligned_alloc - local.tee 2 - i32.eqz - br_if 1 (;@1;) - local.get 2 - local.get 0 - local.get 1 - local.get 3 - local.get 1 - local.get 3 - i32.lt_u - select - call $memcpy - local.set 2 - local.get 0 - call $free - local.get 2 - return - end - local.get 0 - local.get 3 - call $realloc - local.set 4 - end - local.get 4 - ) - (func $malloc (;17;) (type 9) (param i32) (result i32) - local.get 0 - call $dlmalloc - ) - (func $dlmalloc (;18;) (type 9) (param i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - global.get $__stack_pointer - i32.const 16 - i32.sub - local.tee 1 - global.set $__stack_pointer - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - block ;; label = @12 - i32.const 0 - i32.load offset=1048604 - local.tee 2 - br_if 0 (;@12;) - block ;; label = @13 - i32.const 0 - i32.load offset=1049052 - local.tee 3 - br_if 0 (;@13;) - i32.const 0 - i64.const -1 - i64.store offset=1049064 align=4 - i32.const 0 - i64.const 281474976776192 - i64.store offset=1049056 align=4 - i32.const 0 - local.get 1 - i32.const 8 - i32.add - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - local.tee 3 - i32.store offset=1049052 - i32.const 0 - i32.const 0 - i32.store offset=1049072 - i32.const 0 - i32.const 0 - i32.store offset=1049024 - end - i32.const 1114112 - i32.const 1049088 - i32.lt_u - br_if 1 (;@11;) - i32.const 0 - local.set 2 - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.const 89 - i32.lt_u - br_if 0 (;@12;) - i32.const 0 - local.set 4 - i32.const 0 - i32.const 1049088 - i32.store offset=1049028 - i32.const 0 - i32.const 1049088 - i32.store offset=1048596 - i32.const 0 - local.get 3 - i32.store offset=1048616 - i32.const 0 - i32.const -1 - i32.store offset=1048612 - i32.const 0 - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.store offset=1049032 - loop ;; label = @13 - local.get 4 - i32.const 1048640 - i32.add - local.get 4 - i32.const 1048628 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 4 - i32.const 1048620 - i32.add - local.tee 5 - i32.store - local.get 4 - i32.const 1048632 - i32.add - local.get 5 - i32.store - local.get 4 - i32.const 1048648 - i32.add - local.get 4 - i32.const 1048636 - i32.add - local.tee 5 - i32.store - local.get 5 - local.get 3 - i32.store - local.get 4 - i32.const 1048656 - i32.add - local.get 4 - i32.const 1048644 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 5 - i32.store - local.get 4 - i32.const 1048652 - i32.add - local.get 3 - i32.store - local.get 4 - i32.const 32 - i32.add - local.tee 4 - i32.const 256 - i32.ne - br_if 0 (;@13;) - end - i32.const 1049088 - i32.const -8 - i32.const 1049088 - i32.sub - i32.const 15 - i32.and - i32.const 0 - i32.const 1049088 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 2 - i32.const 4 - i32.add - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.const -56 - i32.add - local.tee 3 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 2 - i32.store offset=1048604 - local.get 3 - i32.const 1049088 - i32.add - i32.const 4 - i32.add - i32.const 56 - i32.store - end - block ;; label = @12 - block ;; label = @13 - local.get 0 - i32.const 236 - i32.gt_u - br_if 0 (;@13;) - block ;; label = @14 - i32.const 0 - i32.load offset=1048580 - local.tee 6 - i32.const 16 - local.get 0 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 0 - i32.const 11 - i32.lt_u - select - local.tee 7 - i32.const 3 - i32.shr_u - local.tee 3 - i32.shr_u - local.tee 4 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@14;) - block ;; label = @15 - block ;; label = @16 - local.get 4 - i32.const 1 - i32.and - local.get 3 - i32.or - i32.const 1 - i32.xor - local.tee 5 - i32.const 3 - i32.shl - local.tee 3 - i32.const 1048620 - i32.add - local.tee 4 - local.get 3 - i32.const 1048628 - i32.add - i32.load - local.tee 3 - i32.load offset=8 - local.tee 7 - i32.ne - br_if 0 (;@16;) - i32.const 0 - local.get 6 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 1 (;@15;) - end - local.get 4 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 4 - i32.store offset=12 - end - local.get 3 - i32.const 8 - i32.add - local.set 4 - local.get 3 - local.get 5 - i32.const 3 - i32.shl - local.tee 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 5 - i32.add - local.tee 3 - local.get 3 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 13 (;@1;) - end - local.get 7 - i32.const 0 - i32.load offset=1048588 - local.tee 8 - i32.le_u - br_if 1 (;@12;) - block ;; label = @14 - local.get 4 - i32.eqz - br_if 0 (;@14;) - block ;; label = @15 - block ;; label = @16 - local.get 4 - local.get 3 - i32.shl - i32.const 2 - local.get 3 - i32.shl - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.or - i32.and - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.ctz - local.tee 3 - i32.const 3 - i32.shl - local.tee 4 - i32.const 1048620 - i32.add - local.tee 5 - local.get 4 - i32.const 1048628 - i32.add - i32.load - local.tee 4 - i32.load offset=8 - local.tee 0 - i32.ne - br_if 0 (;@16;) - i32.const 0 - local.get 6 - i32.const -2 - local.get 3 - i32.rotl - i32.and - local.tee 6 - i32.store offset=1048580 - br 1 (;@15;) - end - local.get 5 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 5 - i32.store offset=12 - end - local.get 4 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - i32.const 3 - i32.shl - local.tee 3 - i32.add - local.get 3 - local.get 7 - i32.sub - local.tee 5 - i32.store - local.get 4 - local.get 7 - i32.add - local.tee 0 - local.get 5 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @15 - local.get 8 - i32.eqz - br_if 0 (;@15;) - local.get 8 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 7 - i32.const 0 - i32.load offset=1048600 - local.set 3 - block ;; label = @16 - block ;; label = @17 - local.get 6 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 9 - i32.and - br_if 0 (;@17;) - i32.const 0 - local.get 6 - local.get 9 - i32.or - i32.store offset=1048580 - local.get 7 - local.set 9 - br 1 (;@16;) - end - local.get 7 - i32.load offset=8 - local.set 9 - end - local.get 9 - local.get 3 - i32.store offset=12 - local.get 7 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 7 - i32.store offset=12 - local.get 3 - local.get 9 - i32.store offset=8 - end - local.get 4 - i32.const 8 - i32.add - local.set 4 - i32.const 0 - local.get 0 - i32.store offset=1048600 - i32.const 0 - local.get 5 - i32.store offset=1048588 - br 13 (;@1;) - end - i32.const 0 - i32.load offset=1048584 - local.tee 10 - i32.eqz - br_if 1 (;@12;) - local.get 10 - i32.const 0 - local.get 10 - i32.sub - i32.and - i32.ctz - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.tee 0 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.set 3 - local.get 0 - local.set 5 - block ;; label = @14 - loop ;; label = @15 - block ;; label = @16 - local.get 5 - i32.load offset=16 - local.tee 4 - br_if 0 (;@16;) - local.get 5 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 2 (;@14;) - end - local.get 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 5 - local.get 3 - local.get 5 - local.get 3 - i32.lt_u - local.tee 5 - select - local.set 3 - local.get 4 - local.get 0 - local.get 5 - select - local.set 0 - local.get 4 - local.set 5 - br 0 (;@15;) - end - end - local.get 0 - i32.load offset=24 - local.set 11 - block ;; label = @14 - local.get 0 - i32.load offset=12 - local.tee 9 - local.get 0 - i32.eq - br_if 0 (;@14;) - local.get 0 - i32.load offset=8 - local.tee 4 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 9 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 9 - i32.store offset=12 - br 12 (;@2;) - end - block ;; label = @14 - local.get 0 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@14;) - local.get 0 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 4 (;@10;) - local.get 0 - i32.const 16 - i32.add - local.set 5 - end - loop ;; label = @14 - local.get 5 - local.set 2 - local.get 4 - local.tee 9 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@14;) - local.get 9 - i32.const 16 - i32.add - local.set 5 - local.get 9 - i32.load offset=16 - local.tee 4 - br_if 0 (;@14;) - end - local.get 2 - i32.const 0 - i32.store - br 11 (;@2;) - end - i32.const -1 - local.set 7 - local.get 0 - i32.const -65 - i32.gt_u - br_if 0 (;@12;) - local.get 0 - i32.const 19 - i32.add - local.tee 4 - i32.const -16 - i32.and - local.set 7 - i32.const 0 - i32.load offset=1048584 - local.tee 10 - i32.eqz - br_if 0 (;@12;) - i32.const 0 - local.set 8 - block ;; label = @13 - local.get 7 - i32.const 256 - i32.lt_u - br_if 0 (;@13;) - i32.const 31 - local.set 8 - local.get 7 - i32.const 16777215 - i32.gt_u - br_if 0 (;@13;) - local.get 7 - i32.const 38 - local.get 4 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 8 - end - i32.const 0 - local.get 7 - i32.sub - local.set 3 - block ;; label = @13 - block ;; label = @14 - block ;; label = @15 - block ;; label = @16 - local.get 8 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.tee 5 - br_if 0 (;@16;) - i32.const 0 - local.set 4 - i32.const 0 - local.set 9 - br 1 (;@15;) - end - i32.const 0 - local.set 4 - local.get 7 - i32.const 0 - i32.const 25 - local.get 8 - i32.const 1 - i32.shr_u - i32.sub - local.get 8 - i32.const 31 - i32.eq - select - i32.shl - local.set 0 - i32.const 0 - local.set 9 - loop ;; label = @16 - block ;; label = @17 - local.get 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 6 - local.get 3 - i32.ge_u - br_if 0 (;@17;) - local.get 6 - local.set 3 - local.get 5 - local.set 9 - local.get 6 - br_if 0 (;@17;) - i32.const 0 - local.set 3 - local.get 5 - local.set 9 - local.get 5 - local.set 4 - br 3 (;@14;) - end - local.get 4 - local.get 5 - i32.const 20 - i32.add - i32.load - local.tee 6 - local.get 6 - local.get 5 - local.get 0 - i32.const 29 - i32.shr_u - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - i32.load - local.tee 5 - i32.eq - select - local.get 4 - local.get 6 - select - local.set 4 - local.get 0 - i32.const 1 - i32.shl - local.set 0 - local.get 5 - br_if 0 (;@16;) - end - end - block ;; label = @15 - local.get 4 - local.get 9 - i32.or - br_if 0 (;@15;) - i32.const 0 - local.set 9 - i32.const 2 - local.get 8 - i32.shl - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.or - local.get 10 - i32.and - local.tee 4 - i32.eqz - br_if 3 (;@12;) - local.get 4 - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.ctz - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.set 4 - end - local.get 4 - i32.eqz - br_if 1 (;@13;) - end - loop ;; label = @14 - local.get 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 6 - local.get 3 - i32.lt_u - local.set 0 - block ;; label = @15 - local.get 4 - i32.load offset=16 - local.tee 5 - br_if 0 (;@15;) - local.get 4 - i32.const 20 - i32.add - i32.load - local.set 5 - end - local.get 6 - local.get 3 - local.get 0 - select - local.set 3 - local.get 4 - local.get 9 - local.get 0 - select - local.set 9 - local.get 5 - local.set 4 - local.get 5 - br_if 0 (;@14;) - end - end - local.get 9 - i32.eqz - br_if 0 (;@12;) - local.get 3 - i32.const 0 - i32.load offset=1048588 - local.get 7 - i32.sub - i32.ge_u - br_if 0 (;@12;) - local.get 9 - i32.load offset=24 - local.set 2 - block ;; label = @13 - local.get 9 - i32.load offset=12 - local.tee 0 - local.get 9 - i32.eq - br_if 0 (;@13;) - local.get 9 - i32.load offset=8 - local.tee 4 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 0 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 0 - i32.store offset=12 - br 10 (;@3;) - end - block ;; label = @13 - local.get 9 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@13;) - local.get 9 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 4 (;@9;) - local.get 9 - i32.const 16 - i32.add - local.set 5 - end - loop ;; label = @13 - local.get 5 - local.set 6 - local.get 4 - local.tee 0 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@13;) - local.get 0 - i32.const 16 - i32.add - local.set 5 - local.get 0 - i32.load offset=16 - local.tee 4 - br_if 0 (;@13;) - end - local.get 6 - i32.const 0 - i32.store - br 9 (;@3;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1048588 - local.tee 4 - local.get 7 - i32.lt_u - br_if 0 (;@12;) - i32.const 0 - i32.load offset=1048600 - local.set 3 - block ;; label = @13 - block ;; label = @14 - local.get 4 - local.get 7 - i32.sub - local.tee 5 - i32.const 16 - i32.lt_u - br_if 0 (;@14;) - local.get 3 - local.get 7 - i32.add - local.tee 0 - local.get 5 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 4 - i32.add - local.get 5 - i32.store - local.get 3 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - br 1 (;@13;) - end - local.get 3 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 0 - i32.const 0 - local.set 5 - end - i32.const 0 - local.get 5 - i32.store offset=1048588 - i32.const 0 - local.get 0 - i32.store offset=1048600 - local.get 3 - i32.const 8 - i32.add - local.set 4 - br 11 (;@1;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1048592 - local.tee 5 - local.get 7 - i32.le_u - br_if 0 (;@12;) - local.get 2 - local.get 7 - i32.add - local.tee 4 - local.get 5 - local.get 7 - i32.sub - local.tee 3 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.get 4 - i32.store offset=1048604 - i32.const 0 - local.get 3 - i32.store offset=1048592 - local.get 2 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 2 - i32.const 8 - i32.add - local.set 4 - br 11 (;@1;) - end - block ;; label = @12 - block ;; label = @13 - i32.const 0 - i32.load offset=1049052 - i32.eqz - br_if 0 (;@13;) - i32.const 0 - i32.load offset=1049060 - local.set 3 - br 1 (;@12;) - end - i32.const 0 - i64.const -1 - i64.store offset=1049064 align=4 - i32.const 0 - i64.const 281474976776192 - i64.store offset=1049056 align=4 - i32.const 0 - local.get 1 - i32.const 12 - i32.add - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - i32.store offset=1049052 - i32.const 0 - i32.const 0 - i32.store offset=1049072 - i32.const 0 - i32.const 0 - i32.store offset=1049024 - i32.const 65536 - local.set 3 - end - i32.const 0 - local.set 4 - block ;; label = @12 - local.get 3 - local.get 7 - i32.const 71 - i32.add - local.tee 8 - i32.add - local.tee 0 - i32.const 0 - local.get 3 - i32.sub - local.tee 6 - i32.and - local.tee 9 - local.get 7 - i32.gt_u - br_if 0 (;@12;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 11 (;@1;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1049020 - local.tee 4 - i32.eqz - br_if 0 (;@12;) - block ;; label = @13 - i32.const 0 - i32.load offset=1049012 - local.tee 3 - local.get 9 - i32.add - local.tee 10 - local.get 3 - i32.le_u - br_if 0 (;@13;) - local.get 10 - local.get 4 - i32.le_u - br_if 1 (;@12;) - end - i32.const 0 - local.set 4 - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 11 (;@1;) - end - i32.const 0 - i32.load8_u offset=1049024 - i32.const 4 - i32.and - br_if 5 (;@6;) - block ;; label = @12 - block ;; label = @13 - block ;; label = @14 - local.get 2 - i32.eqz - br_if 0 (;@14;) - i32.const 1049028 - local.set 4 - loop ;; label = @15 - block ;; label = @16 - local.get 4 - i32.load - local.tee 3 - local.get 2 - i32.gt_u - br_if 0 (;@16;) - local.get 3 - local.get 4 - i32.load offset=4 - i32.add - local.get 2 - i32.gt_u - br_if 3 (;@13;) - end - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@15;) - end - end - i32.const 0 - call $sbrk - local.tee 0 - i32.const -1 - i32.eq - br_if 6 (;@7;) - local.get 9 - local.set 6 - block ;; label = @14 - i32.const 0 - i32.load offset=1049056 - local.tee 4 - i32.const -1 - i32.add - local.tee 3 - local.get 0 - i32.and - i32.eqz - br_if 0 (;@14;) - local.get 9 - local.get 0 - i32.sub - local.get 3 - local.get 0 - i32.add - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.add - local.set 6 - end - local.get 6 - local.get 7 - i32.le_u - br_if 6 (;@7;) - local.get 6 - i32.const 2147483646 - i32.gt_u - br_if 6 (;@7;) - block ;; label = @14 - i32.const 0 - i32.load offset=1049020 - local.tee 4 - i32.eqz - br_if 0 (;@14;) - i32.const 0 - i32.load offset=1049012 - local.tee 3 - local.get 6 - i32.add - local.tee 5 - local.get 3 - i32.le_u - br_if 7 (;@7;) - local.get 5 - local.get 4 - i32.gt_u - br_if 7 (;@7;) - end - local.get 6 - call $sbrk - local.tee 4 - local.get 0 - i32.ne - br_if 1 (;@12;) - br 8 (;@5;) - end - local.get 0 - local.get 5 - i32.sub - local.get 6 - i32.and - local.tee 6 - i32.const 2147483646 - i32.gt_u - br_if 5 (;@7;) - local.get 6 - call $sbrk - local.tee 0 - local.get 4 - i32.load - local.get 4 - i32.load offset=4 - i32.add - i32.eq - br_if 4 (;@8;) - local.get 0 - local.set 4 - end - block ;; label = @12 - local.get 4 - i32.const -1 - i32.eq - br_if 0 (;@12;) - local.get 7 - i32.const 72 - i32.add - local.get 6 - i32.le_u - br_if 0 (;@12;) - block ;; label = @13 - local.get 8 - local.get 6 - i32.sub - i32.const 0 - i32.load offset=1049060 - local.tee 3 - i32.add - i32.const 0 - local.get 3 - i32.sub - i32.and - local.tee 3 - i32.const 2147483646 - i32.le_u - br_if 0 (;@13;) - local.get 4 - local.set 0 - br 8 (;@5;) - end - block ;; label = @13 - local.get 3 - call $sbrk - i32.const -1 - i32.eq - br_if 0 (;@13;) - local.get 3 - local.get 6 - i32.add - local.set 6 - local.get 4 - local.set 0 - br 8 (;@5;) - end - i32.const 0 - local.get 6 - i32.sub - call $sbrk - drop - br 5 (;@7;) - end - local.get 4 - local.set 0 - local.get 4 - i32.const -1 - i32.ne - br_if 6 (;@5;) - br 4 (;@7;) - end - unreachable - unreachable - end - i32.const 0 - local.set 9 - br 7 (;@2;) - end - i32.const 0 - local.set 0 - br 5 (;@3;) - end - local.get 0 - i32.const -1 - i32.ne - br_if 2 (;@5;) - end - i32.const 0 - i32.const 0 - i32.load offset=1049024 - i32.const 4 - i32.or - i32.store offset=1049024 - end - local.get 9 - i32.const 2147483646 - i32.gt_u - br_if 1 (;@4;) - local.get 9 - call $sbrk - local.set 0 - i32.const 0 - call $sbrk - local.set 4 - local.get 0 - i32.const -1 - i32.eq - br_if 1 (;@4;) - local.get 4 - i32.const -1 - i32.eq - br_if 1 (;@4;) - local.get 0 - local.get 4 - i32.ge_u - br_if 1 (;@4;) - local.get 4 - local.get 0 - i32.sub - local.tee 6 - local.get 7 - i32.const 56 - i32.add - i32.le_u - br_if 1 (;@4;) - end - i32.const 0 - i32.const 0 - i32.load offset=1049012 - local.get 6 - i32.add - local.tee 4 - i32.store offset=1049012 - block ;; label = @5 - local.get 4 - i32.const 0 - i32.load offset=1049016 - i32.le_u - br_if 0 (;@5;) - i32.const 0 - local.get 4 - i32.store offset=1049016 - end - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - i32.const 0 - i32.load offset=1048604 - local.tee 3 - i32.eqz - br_if 0 (;@8;) - i32.const 1049028 - local.set 4 - loop ;; label = @9 - local.get 0 - local.get 4 - i32.load - local.tee 5 - local.get 4 - i32.load offset=4 - local.tee 9 - i32.add - i32.eq - br_if 2 (;@7;) - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@9;) - br 3 (;@6;) - end - end - block ;; label = @8 - block ;; label = @9 - i32.const 0 - i32.load offset=1048596 - local.tee 4 - i32.eqz - br_if 0 (;@9;) - local.get 0 - local.get 4 - i32.ge_u - br_if 1 (;@8;) - end - i32.const 0 - local.get 0 - i32.store offset=1048596 - end - i32.const 0 - local.set 4 - i32.const 0 - local.get 6 - i32.store offset=1049032 - i32.const 0 - local.get 0 - i32.store offset=1049028 - i32.const 0 - i32.const -1 - i32.store offset=1048612 - i32.const 0 - i32.const 0 - i32.load offset=1049052 - i32.store offset=1048616 - i32.const 0 - i32.const 0 - i32.store offset=1049040 - loop ;; label = @8 - local.get 4 - i32.const 1048640 - i32.add - local.get 4 - i32.const 1048628 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 4 - i32.const 1048620 - i32.add - local.tee 5 - i32.store - local.get 4 - i32.const 1048632 - i32.add - local.get 5 - i32.store - local.get 4 - i32.const 1048648 - i32.add - local.get 4 - i32.const 1048636 - i32.add - local.tee 5 - i32.store - local.get 5 - local.get 3 - i32.store - local.get 4 - i32.const 1048656 - i32.add - local.get 4 - i32.const 1048644 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 5 - i32.store - local.get 4 - i32.const 1048652 - i32.add - local.get 3 - i32.store - local.get 4 - i32.const 32 - i32.add - local.tee 4 - i32.const 256 - i32.ne - br_if 0 (;@8;) - end - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 3 - local.get 6 - i32.const -56 - i32.add - local.tee 5 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 3 - i32.store offset=1048604 - local.get 0 - local.get 5 - i32.add - i32.const 56 - i32.store offset=4 - br 2 (;@5;) - end - local.get 4 - i32.load8_u offset=12 - i32.const 8 - i32.and - br_if 0 (;@6;) - local.get 3 - local.get 5 - i32.lt_u - br_if 0 (;@6;) - local.get 3 - local.get 0 - i32.ge_u - br_if 0 (;@6;) - local.get 3 - i32.const -8 - local.get 3 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 3 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 5 - i32.add - local.tee 0 - i32.const 0 - i32.load offset=1048592 - local.get 6 - i32.add - local.tee 2 - local.get 5 - i32.sub - local.tee 5 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 9 - local.get 6 - i32.add - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 5 - i32.store offset=1048592 - i32.const 0 - local.get 0 - i32.store offset=1048604 - local.get 3 - local.get 2 - i32.add - i32.const 56 - i32.store offset=4 - br 1 (;@5;) - end - block ;; label = @6 - local.get 0 - i32.const 0 - i32.load offset=1048596 - local.tee 9 - i32.ge_u - br_if 0 (;@6;) - i32.const 0 - local.get 0 - i32.store offset=1048596 - local.get 0 - local.set 9 - end - local.get 0 - local.get 6 - i32.add - local.set 5 - i32.const 1049028 - local.set 4 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - block ;; label = @12 - loop ;; label = @13 - local.get 4 - i32.load - local.get 5 - i32.eq - br_if 1 (;@12;) - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@13;) - br 2 (;@11;) - end - end - local.get 4 - i32.load8_u offset=12 - i32.const 8 - i32.and - i32.eqz - br_if 1 (;@10;) - end - i32.const 1049028 - local.set 4 - loop ;; label = @11 - block ;; label = @12 - local.get 4 - i32.load - local.tee 5 - local.get 3 - i32.gt_u - br_if 0 (;@12;) - local.get 5 - local.get 4 - i32.load offset=4 - i32.add - local.tee 5 - local.get 3 - i32.gt_u - br_if 3 (;@9;) - end - local.get 4 - i32.load offset=8 - local.set 4 - br 0 (;@11;) - end - end - local.get 4 - local.get 0 - i32.store - local.get 4 - local.get 4 - i32.load offset=4 - local.get 6 - i32.add - i32.store offset=4 - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - i32.add - local.tee 2 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 5 - i32.const -8 - local.get 5 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 5 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - i32.add - local.tee 6 - local.get 2 - local.get 7 - i32.add - local.tee 7 - i32.sub - local.set 4 - block ;; label = @10 - local.get 6 - local.get 3 - i32.ne - br_if 0 (;@10;) - i32.const 0 - local.get 7 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 4 - i32.add - local.tee 4 - i32.store offset=1048592 - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - br 3 (;@7;) - end - block ;; label = @10 - local.get 6 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@10;) - i32.const 0 - local.get 7 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 4 - i32.add - local.tee 4 - i32.store offset=1048588 - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 7 - local.get 4 - i32.add - local.get 4 - i32.store - br 3 (;@7;) - end - block ;; label = @10 - local.get 6 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 1 - i32.ne - br_if 0 (;@10;) - local.get 3 - i32.const -8 - i32.and - local.set 8 - block ;; label = @11 - block ;; label = @12 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@12;) - local.get 6 - i32.load offset=8 - local.tee 5 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 9 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 0 - i32.eq - drop - block ;; label = @13 - local.get 6 - i32.load offset=12 - local.tee 3 - local.get 5 - i32.ne - br_if 0 (;@13;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 9 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@11;) - end - local.get 3 - local.get 0 - i32.eq - drop - local.get 3 - local.get 5 - i32.store offset=8 - local.get 5 - local.get 3 - i32.store offset=12 - br 1 (;@11;) - end - local.get 6 - i32.load offset=24 - local.set 10 - block ;; label = @12 - block ;; label = @13 - local.get 6 - i32.load offset=12 - local.tee 0 - local.get 6 - i32.eq - br_if 0 (;@13;) - local.get 6 - i32.load offset=8 - local.tee 3 - local.get 9 - i32.lt_u - drop - local.get 0 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 0 - i32.store offset=12 - br 1 (;@12;) - end - block ;; label = @13 - local.get 6 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - local.get 6 - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - i32.const 0 - local.set 0 - br 1 (;@12;) - end - loop ;; label = @13 - local.get 3 - local.set 9 - local.get 5 - local.tee 0 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - local.get 0 - i32.const 16 - i32.add - local.set 3 - local.get 0 - i32.load offset=16 - local.tee 5 - br_if 0 (;@13;) - end - local.get 9 - i32.const 0 - i32.store - end - local.get 10 - i32.eqz - br_if 0 (;@11;) - block ;; label = @12 - block ;; label = @13 - local.get 6 - local.get 6 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@13;) - local.get 3 - local.get 0 - i32.store - local.get 0 - br_if 1 (;@12;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@11;) - end - local.get 10 - i32.const 16 - i32.const 20 - local.get 10 - i32.load offset=16 - local.get 6 - i32.eq - select - i32.add - local.get 0 - i32.store - local.get 0 - i32.eqz - br_if 1 (;@11;) - end - local.get 0 - local.get 10 - i32.store offset=24 - block ;; label = @12 - local.get 6 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@12;) - local.get 0 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 0 - i32.store offset=24 - end - local.get 6 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 0 (;@11;) - local.get 0 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 0 - i32.store offset=24 - end - local.get 8 - local.get 4 - i32.add - local.set 4 - local.get 6 - local.get 8 - i32.add - local.tee 6 - i32.load offset=4 - local.set 3 - end - local.get 6 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 7 - local.get 4 - i32.add - local.get 4 - i32.store - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @10 - local.get 4 - i32.const 255 - i32.gt_u - br_if 0 (;@10;) - local.get 4 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 3 - block ;; label = @11 - block ;; label = @12 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 4 - i32.const 3 - i32.shr_u - i32.shl - local.tee 4 - i32.and - br_if 0 (;@12;) - i32.const 0 - local.get 5 - local.get 4 - i32.or - i32.store offset=1048580 - local.get 3 - local.set 4 - br 1 (;@11;) - end - local.get 3 - i32.load offset=8 - local.set 4 - end - local.get 4 - local.get 7 - i32.store offset=12 - local.get 3 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 3 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - br 3 (;@7;) - end - i32.const 31 - local.set 3 - block ;; label = @10 - local.get 4 - i32.const 16777215 - i32.gt_u - br_if 0 (;@10;) - local.get 4 - i32.const 38 - local.get 4 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 7 - local.get 3 - i32.store offset=28 - local.get 7 - i64.const 0 - i64.store offset=16 align=4 - local.get 3 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @10 - i32.const 0 - i32.load offset=1048584 - local.tee 0 - i32.const 1 - local.get 3 - i32.shl - local.tee 9 - i32.and - br_if 0 (;@10;) - local.get 5 - local.get 7 - i32.store - i32.const 0 - local.get 0 - local.get 9 - i32.or - i32.store offset=1048584 - local.get 7 - local.get 5 - i32.store offset=24 - local.get 7 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 7 - i32.store offset=12 - br 3 (;@7;) - end - local.get 4 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 5 - i32.load - local.set 0 - loop ;; label = @10 - local.get 0 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 4 - i32.eq - br_if 2 (;@8;) - local.get 3 - i32.const 29 - i32.shr_u - local.set 0 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 5 - local.get 0 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 9 - i32.load - local.tee 0 - br_if 0 (;@10;) - end - local.get 9 - local.get 7 - i32.store - local.get 7 - local.get 5 - i32.store offset=24 - local.get 7 - local.get 7 - i32.store offset=12 - local.get 7 - local.get 7 - i32.store offset=8 - br 2 (;@7;) - end - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 2 - local.get 6 - i32.const -56 - i32.add - local.tee 9 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 9 - i32.add - i32.const 56 - i32.store offset=4 - local.get 3 - local.get 5 - i32.const 55 - local.get 5 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 5 - i32.const -55 - i32.add - i32.const 15 - i32.and - select - i32.add - i32.const -63 - i32.add - local.tee 9 - local.get 9 - local.get 3 - i32.const 16 - i32.add - i32.lt_u - select - local.tee 9 - i32.const 35 - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 2 - i32.store offset=1048604 - local.get 9 - i32.const 16 - i32.add - i32.const 0 - i64.load offset=1049036 align=4 - i64.store align=4 - local.get 9 - i32.const 0 - i64.load offset=1049028 align=4 - i64.store offset=8 align=4 - i32.const 0 - local.get 9 - i32.const 8 - i32.add - i32.store offset=1049036 - i32.const 0 - local.get 6 - i32.store offset=1049032 - i32.const 0 - local.get 0 - i32.store offset=1049028 - i32.const 0 - i32.const 0 - i32.store offset=1049040 - local.get 9 - i32.const 36 - i32.add - local.set 4 - loop ;; label = @9 - local.get 4 - i32.const 7 - i32.store - local.get 4 - i32.const 4 - i32.add - local.tee 4 - local.get 5 - i32.lt_u - br_if 0 (;@9;) - end - local.get 9 - local.get 3 - i32.eq - br_if 3 (;@5;) - local.get 9 - local.get 9 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - local.get 9 - local.get 9 - local.get 3 - i32.sub - local.tee 0 - i32.store - local.get 3 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @9 - local.get 0 - i32.const 255 - i32.gt_u - br_if 0 (;@9;) - local.get 0 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 4 - block ;; label = @10 - block ;; label = @11 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 0 - i32.const 3 - i32.shr_u - i32.shl - local.tee 0 - i32.and - br_if 0 (;@11;) - i32.const 0 - local.get 5 - local.get 0 - i32.or - i32.store offset=1048580 - local.get 4 - local.set 5 - br 1 (;@10;) - end - local.get 4 - i32.load offset=8 - local.set 5 - end - local.get 5 - local.get 3 - i32.store offset=12 - local.get 4 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 4 - i32.store offset=12 - local.get 3 - local.get 5 - i32.store offset=8 - br 4 (;@5;) - end - i32.const 31 - local.set 4 - block ;; label = @9 - local.get 0 - i32.const 16777215 - i32.gt_u - br_if 0 (;@9;) - local.get 0 - i32.const 38 - local.get 0 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 4 - end - local.get 3 - local.get 4 - i32.store offset=28 - local.get 3 - i64.const 0 - i64.store offset=16 align=4 - local.get 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @9 - i32.const 0 - i32.load offset=1048584 - local.tee 9 - i32.const 1 - local.get 4 - i32.shl - local.tee 6 - i32.and - br_if 0 (;@9;) - local.get 5 - local.get 3 - i32.store - i32.const 0 - local.get 9 - local.get 6 - i32.or - i32.store offset=1048584 - local.get 3 - local.get 5 - i32.store offset=24 - local.get 3 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 3 - i32.store offset=12 - br 4 (;@5;) - end - local.get 0 - i32.const 0 - i32.const 25 - local.get 4 - i32.const 1 - i32.shr_u - i32.sub - local.get 4 - i32.const 31 - i32.eq - select - i32.shl - local.set 4 - local.get 5 - i32.load - local.set 9 - loop ;; label = @9 - local.get 9 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 0 - i32.eq - br_if 3 (;@6;) - local.get 4 - i32.const 29 - i32.shr_u - local.set 9 - local.get 4 - i32.const 1 - i32.shl - local.set 4 - local.get 5 - local.get 9 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 6 - i32.load - local.tee 9 - br_if 0 (;@9;) - end - local.get 6 - local.get 3 - i32.store - local.get 3 - local.get 5 - i32.store offset=24 - local.get 3 - local.get 3 - i32.store offset=12 - local.get 3 - local.get 3 - i32.store offset=8 - br 3 (;@5;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 7 - i32.store offset=12 - local.get 5 - local.get 7 - i32.store offset=8 - local.get 7 - i32.const 0 - i32.store offset=24 - local.get 7 - local.get 5 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - end - local.get 2 - i32.const 8 - i32.add - local.set 4 - br 5 (;@1;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.store offset=12 - local.get 5 - local.get 3 - i32.store offset=8 - local.get 3 - i32.const 0 - i32.store offset=24 - local.get 3 - local.get 5 - i32.store offset=12 - local.get 3 - local.get 4 - i32.store offset=8 - end - i32.const 0 - i32.load offset=1048592 - local.tee 4 - local.get 7 - i32.le_u - br_if 0 (;@4;) - i32.const 0 - i32.load offset=1048604 - local.tee 3 - local.get 7 - i32.add - local.tee 5 - local.get 4 - local.get 7 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 5 - i32.store offset=1048604 - local.get 3 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - i32.const 8 - i32.add - local.set 4 - br 3 (;@1;) - end - i32.const 0 - local.set 4 - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 2 (;@1;) - end - block ;; label = @3 - local.get 2 - i32.eqz - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 9 - local.get 9 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 4 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 4 - local.get 0 - i32.store - local.get 0 - br_if 1 (;@4;) - i32.const 0 - local.get 10 - i32.const -2 - local.get 5 - i32.rotl - i32.and - local.tee 10 - i32.store offset=1048584 - br 2 (;@3;) - end - local.get 2 - i32.const 16 - i32.const 20 - local.get 2 - i32.load offset=16 - local.get 9 - i32.eq - select - i32.add - local.get 0 - i32.store - local.get 0 - i32.eqz - br_if 1 (;@3;) - end - local.get 0 - local.get 2 - i32.store offset=24 - block ;; label = @4 - local.get 9 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 0 (;@4;) - local.get 0 - local.get 4 - i32.store offset=16 - local.get 4 - local.get 0 - i32.store offset=24 - end - local.get 9 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@3;) - local.get 0 - i32.const 20 - i32.add - local.get 4 - i32.store - local.get 4 - local.get 0 - i32.store offset=24 - end - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 15 - i32.gt_u - br_if 0 (;@4;) - local.get 9 - local.get 3 - local.get 7 - i32.add - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 9 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 1 (;@3;) - end - local.get 9 - local.get 7 - i32.add - local.tee 0 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 9 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @4 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 3 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 4 - block ;; label = @5 - block ;; label = @6 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 3 - i32.const 3 - i32.shr_u - i32.shl - local.tee 3 - i32.and - br_if 0 (;@6;) - i32.const 0 - local.get 5 - local.get 3 - i32.or - i32.store offset=1048580 - local.get 4 - local.set 3 - br 1 (;@5;) - end - local.get 4 - i32.load offset=8 - local.set 3 - end - local.get 3 - local.get 0 - i32.store offset=12 - local.get 4 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 4 - i32.store offset=12 - local.get 0 - local.get 3 - i32.store offset=8 - br 1 (;@3;) - end - i32.const 31 - local.set 4 - block ;; label = @4 - local.get 3 - i32.const 16777215 - i32.gt_u - br_if 0 (;@4;) - local.get 3 - i32.const 38 - local.get 3 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 4 - end - local.get 0 - local.get 4 - i32.store offset=28 - local.get 0 - i64.const 0 - i64.store offset=16 align=4 - local.get 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @4 - local.get 10 - i32.const 1 - local.get 4 - i32.shl - local.tee 7 - i32.and - br_if 0 (;@4;) - local.get 5 - local.get 0 - i32.store - i32.const 0 - local.get 10 - local.get 7 - i32.or - i32.store offset=1048584 - local.get 0 - local.get 5 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 0 - i32.store offset=12 - br 1 (;@3;) - end - local.get 3 - i32.const 0 - i32.const 25 - local.get 4 - i32.const 1 - i32.shr_u - i32.sub - local.get 4 - i32.const 31 - i32.eq - select - i32.shl - local.set 4 - local.get 5 - i32.load - local.set 7 - block ;; label = @4 - loop ;; label = @5 - local.get 7 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 3 - i32.eq - br_if 1 (;@4;) - local.get 4 - i32.const 29 - i32.shr_u - local.set 7 - local.get 4 - i32.const 1 - i32.shl - local.set 4 - local.get 5 - local.get 7 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 6 - i32.load - local.tee 7 - br_if 0 (;@5;) - end - local.get 6 - local.get 0 - i32.store - local.get 0 - local.get 5 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=12 - local.get 0 - local.get 0 - i32.store offset=8 - br 1 (;@3;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 0 - i32.store offset=12 - local.get 5 - local.get 0 - i32.store offset=8 - local.get 0 - i32.const 0 - i32.store offset=24 - local.get 0 - local.get 5 - i32.store offset=12 - local.get 0 - local.get 4 - i32.store offset=8 - end - local.get 9 - i32.const 8 - i32.add - local.set 4 - br 1 (;@1;) - end - block ;; label = @2 - local.get 11 - i32.eqz - br_if 0 (;@2;) - block ;; label = @3 - block ;; label = @4 - local.get 0 - local.get 0 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 4 - i32.load - i32.ne - br_if 0 (;@4;) - local.get 4 - local.get 9 - i32.store - local.get 9 - br_if 1 (;@3;) - i32.const 0 - local.get 10 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@2;) - end - local.get 11 - i32.const 16 - i32.const 20 - local.get 11 - i32.load offset=16 - local.get 0 - i32.eq - select - i32.add - local.get 9 - i32.store - local.get 9 - i32.eqz - br_if 1 (;@2;) - end - local.get 9 - local.get 11 - i32.store offset=24 - block ;; label = @3 - local.get 0 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 0 (;@3;) - local.get 9 - local.get 4 - i32.store offset=16 - local.get 4 - local.get 9 - i32.store offset=24 - end - local.get 0 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@2;) - local.get 9 - i32.const 20 - i32.add - local.get 4 - i32.store - local.get 4 - local.get 9 - i32.store offset=24 - end - block ;; label = @2 - block ;; label = @3 - local.get 3 - i32.const 15 - i32.gt_u - br_if 0 (;@3;) - local.get 0 - local.get 3 - local.get 7 - i32.add - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 1 (;@2;) - end - local.get 0 - local.get 7 - i32.add - local.tee 5 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 5 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @3 - local.get 8 - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 7 - i32.const 0 - i32.load offset=1048600 - local.set 4 - block ;; label = @4 - block ;; label = @5 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 9 - local.get 6 - i32.and - br_if 0 (;@5;) - i32.const 0 - local.get 9 - local.get 6 - i32.or - i32.store offset=1048580 - local.get 7 - local.set 9 - br 1 (;@4;) - end - local.get 7 - i32.load offset=8 - local.set 9 - end - local.get 9 - local.get 4 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 7 - i32.store offset=12 - local.get 4 - local.get 9 - i32.store offset=8 - end - i32.const 0 - local.get 5 - i32.store offset=1048600 - i32.const 0 - local.get 3 - i32.store offset=1048588 - end - local.get 0 - i32.const 8 - i32.add - local.set 4 - end - local.get 1 - i32.const 16 - i32.add - global.set $__stack_pointer - local.get 4 - ) - (func $free (;19;) (type 0) (param i32) - local.get 0 - call $dlfree - ) - (func $dlfree (;20;) (type 0) (param i32) - (local i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 0 - i32.eqz - br_if 0 (;@1;) - local.get 0 - i32.const -8 - i32.add - local.tee 1 - local.get 0 - i32.const -4 - i32.add - i32.load - local.tee 2 - i32.const -8 - i32.and - local.tee 0 - i32.add - local.set 3 - block ;; label = @2 - local.get 2 - i32.const 1 - i32.and - br_if 0 (;@2;) - local.get 2 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 1 - local.get 1 - i32.load - local.tee 2 - i32.sub - local.tee 1 - i32.const 0 - i32.load offset=1048596 - local.tee 4 - i32.lt_u - br_if 1 (;@1;) - local.get 2 - local.get 0 - i32.add - local.set 0 - block ;; label = @3 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.eq - br_if 0 (;@3;) - block ;; label = @4 - local.get 2 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 1 - i32.load offset=8 - local.tee 4 - local.get 2 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @5 - local.get 1 - i32.load offset=12 - local.tee 2 - local.get 4 - i32.ne - br_if 0 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 3 (;@2;) - end - local.get 2 - local.get 6 - i32.eq - drop - local.get 2 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 2 - i32.store offset=12 - br 2 (;@2;) - end - local.get 1 - i32.load offset=24 - local.set 7 - block ;; label = @4 - block ;; label = @5 - local.get 1 - i32.load offset=12 - local.tee 6 - local.get 1 - i32.eq - br_if 0 (;@5;) - local.get 1 - i32.load offset=8 - local.tee 2 - local.get 4 - i32.lt_u - drop - local.get 6 - local.get 2 - i32.store offset=8 - local.get 2 - local.get 6 - i32.store offset=12 - br 1 (;@4;) - end - block ;; label = @5 - local.get 1 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - local.get 1 - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - i32.const 0 - local.set 6 - br 1 (;@4;) - end - loop ;; label = @5 - local.get 2 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - local.get 6 - i32.const 16 - i32.add - local.set 2 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@5;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 1 - local.get 1 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 2 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 2 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@4;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 3 (;@2;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 1 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 2 (;@2;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @4 - local.get 1 - i32.load offset=16 - local.tee 2 - i32.eqz - br_if 0 (;@4;) - local.get 6 - local.get 2 - i32.store offset=16 - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 1 - i32.load offset=20 - local.tee 2 - i32.eqz - br_if 1 (;@2;) - local.get 6 - i32.const 20 - i32.add - local.get 2 - i32.store - local.get 2 - local.get 6 - i32.store offset=24 - br 1 (;@2;) - end - local.get 3 - i32.load offset=4 - local.tee 2 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 0 (;@2;) - local.get 3 - local.get 2 - i32.const -2 - i32.and - i32.store offset=4 - i32.const 0 - local.get 0 - i32.store offset=1048588 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - return - end - local.get 1 - local.get 3 - i32.ge_u - br_if 0 (;@1;) - local.get 3 - i32.load offset=4 - local.tee 2 - i32.const 1 - i32.and - i32.eqz - br_if 0 (;@1;) - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 2 - i32.and - br_if 0 (;@3;) - block ;; label = @4 - local.get 3 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 1 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 0 - i32.add - local.tee 0 - i32.store offset=1048592 - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 3 (;@1;) - i32.const 0 - i32.const 0 - i32.store offset=1048588 - i32.const 0 - i32.const 0 - i32.store offset=1048600 - return - end - block ;; label = @4 - local.get 3 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 1 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 0 - i32.add - local.tee 0 - i32.store offset=1048588 - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - return - end - local.get 2 - i32.const -8 - i32.and - local.get 0 - i32.add - local.set 0 - block ;; label = @4 - block ;; label = @5 - local.get 2 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 3 - i32.load offset=8 - local.tee 4 - local.get 2 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @6 - local.get 3 - i32.load offset=12 - local.tee 2 - local.get 4 - i32.ne - br_if 0 (;@6;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@4;) - end - local.get 2 - local.get 6 - i32.eq - drop - local.get 2 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 2 - i32.store offset=12 - br 1 (;@4;) - end - local.get 3 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.load offset=12 - local.tee 6 - local.get 3 - i32.eq - br_if 0 (;@6;) - local.get 3 - i32.load offset=8 - local.tee 2 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 2 - i32.store offset=8 - local.get 2 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 3 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 3 - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 2 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 2 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 0 (;@4;) - block ;; label = @5 - block ;; label = @6 - local.get 3 - local.get 3 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 2 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 2 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@4;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 3 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 1 (;@4;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 3 - i32.load offset=16 - local.tee 2 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 2 - i32.store offset=16 - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 3 - i32.load offset=20 - local.tee 2 - i32.eqz - br_if 0 (;@4;) - local.get 6 - i32.const 20 - i32.add - local.get 2 - i32.store - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 1 (;@2;) - i32.const 0 - local.get 0 - i32.store offset=1048588 - return - end - local.get 3 - local.get 2 - i32.const -2 - i32.and - i32.store offset=4 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - end - block ;; label = @2 - local.get 0 - i32.const 255 - i32.gt_u - br_if 0 (;@2;) - local.get 0 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 2 - block ;; label = @3 - block ;; label = @4 - i32.const 0 - i32.load offset=1048580 - local.tee 4 - i32.const 1 - local.get 0 - i32.const 3 - i32.shr_u - i32.shl - local.tee 0 - i32.and - br_if 0 (;@4;) - i32.const 0 - local.get 4 - local.get 0 - i32.or - i32.store offset=1048580 - local.get 2 - local.set 0 - br 1 (;@3;) - end - local.get 2 - i32.load offset=8 - local.set 0 - end - local.get 0 - local.get 1 - i32.store offset=12 - local.get 2 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 2 - i32.store offset=12 - local.get 1 - local.get 0 - i32.store offset=8 - return - end - i32.const 31 - local.set 2 - block ;; label = @2 - local.get 0 - i32.const 16777215 - i32.gt_u - br_if 0 (;@2;) - local.get 0 - i32.const 38 - local.get 0 - i32.const 8 - i32.shr_u - i32.clz - local.tee 2 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 2 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 2 - end - local.get 1 - local.get 2 - i32.store offset=28 - local.get 1 - i64.const 0 - i64.store offset=16 align=4 - local.get 2 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 4 - block ;; label = @2 - block ;; label = @3 - i32.const 0 - i32.load offset=1048584 - local.tee 6 - i32.const 1 - local.get 2 - i32.shl - local.tee 3 - i32.and - br_if 0 (;@3;) - local.get 4 - local.get 1 - i32.store - i32.const 0 - local.get 6 - local.get 3 - i32.or - i32.store offset=1048584 - local.get 1 - local.get 4 - i32.store offset=24 - local.get 1 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 1 - i32.store offset=12 - br 1 (;@2;) - end - local.get 0 - i32.const 0 - i32.const 25 - local.get 2 - i32.const 1 - i32.shr_u - i32.sub - local.get 2 - i32.const 31 - i32.eq - select - i32.shl - local.set 2 - local.get 4 - i32.load - local.set 6 - block ;; label = @3 - loop ;; label = @4 - local.get 6 - local.tee 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 0 - i32.eq - br_if 1 (;@3;) - local.get 2 - i32.const 29 - i32.shr_u - local.set 6 - local.get 2 - i32.const 1 - i32.shl - local.set 2 - local.get 4 - local.get 6 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 6 - br_if 0 (;@4;) - end - local.get 3 - local.get 1 - i32.store - local.get 1 - local.get 4 - i32.store offset=24 - local.get 1 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 1 - i32.store offset=8 - br 1 (;@2;) - end - local.get 4 - i32.load offset=8 - local.tee 0 - local.get 1 - i32.store offset=12 - local.get 4 - local.get 1 - i32.store offset=8 - local.get 1 - i32.const 0 - i32.store offset=24 - local.get 1 - local.get 4 - i32.store offset=12 - local.get 1 - local.get 0 - i32.store offset=8 - end - i32.const 0 - i32.const 0 - i32.load offset=1048612 - i32.const -1 - i32.add - local.tee 1 - i32.const -1 - local.get 1 - select - i32.store offset=1048612 - end - ) - (func $realloc (;21;) (type 6) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 0 - br_if 0 (;@1;) - local.get 1 - call $dlmalloc - return - end - block ;; label = @1 - local.get 1 - i32.const -64 - i32.lt_u - br_if 0 (;@1;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const 0 - return - end - i32.const 16 - local.get 1 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.set 2 - local.get 0 - i32.const -4 - i32.add - local.tee 3 - i32.load - local.tee 4 - i32.const -8 - i32.and - local.set 5 - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 4 - i32.const 3 - i32.and - br_if 0 (;@3;) - local.get 2 - i32.const 256 - i32.lt_u - br_if 1 (;@2;) - local.get 5 - local.get 2 - i32.const 4 - i32.or - i32.lt_u - br_if 1 (;@2;) - local.get 5 - local.get 2 - i32.sub - i32.const 0 - i32.load offset=1049060 - i32.const 1 - i32.shl - i32.le_u - br_if 2 (;@1;) - br 1 (;@2;) - end - local.get 0 - i32.const -8 - i32.add - local.tee 6 - local.get 5 - i32.add - local.set 7 - block ;; label = @3 - local.get 5 - local.get 2 - i32.lt_u - br_if 0 (;@3;) - local.get 5 - local.get 2 - i32.sub - local.tee 1 - i32.const 16 - i32.lt_u - br_if 2 (;@1;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 2 - local.get 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 7 - local.get 7 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 1 - call $dispose_chunk - local.get 0 - return - end - block ;; label = @3 - local.get 7 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1048592 - local.get 5 - i32.add - local.tee 5 - local.get 2 - i32.le_u - br_if 1 (;@2;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - i32.const 0 - local.get 6 - local.get 2 - i32.add - local.tee 1 - i32.store offset=1048604 - i32.const 0 - local.get 5 - local.get 2 - i32.sub - local.tee 2 - i32.store offset=1048592 - local.get 1 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - block ;; label = @3 - local.get 7 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1048588 - local.get 5 - i32.add - local.tee 5 - local.get 2 - i32.lt_u - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 5 - local.get 2 - i32.sub - local.tee 1 - i32.const 16 - i32.lt_u - br_if 0 (;@5;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 2 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 6 - local.get 5 - i32.add - local.tee 5 - local.get 1 - i32.store - local.get 5 - local.get 5 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - br 1 (;@4;) - end - local.get 3 - local.get 4 - i32.const 1 - i32.and - local.get 5 - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 5 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 1 - i32.const 0 - local.set 2 - end - i32.const 0 - local.get 2 - i32.store offset=1048600 - i32.const 0 - local.get 1 - i32.store offset=1048588 - local.get 0 - return - end - local.get 7 - i32.load offset=4 - local.tee 8 - i32.const 2 - i32.and - br_if 0 (;@2;) - local.get 8 - i32.const -8 - i32.and - local.get 5 - i32.add - local.tee 9 - local.get 2 - i32.lt_u - br_if 0 (;@2;) - local.get 9 - local.get 2 - i32.sub - local.set 10 - block ;; label = @3 - block ;; label = @4 - local.get 8 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 7 - i32.load offset=8 - local.tee 1 - local.get 8 - i32.const 3 - i32.shr_u - local.tee 11 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 8 - i32.eq - drop - block ;; label = @5 - local.get 7 - i32.load offset=12 - local.tee 5 - local.get 1 - i32.ne - br_if 0 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 11 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@3;) - end - local.get 5 - local.get 8 - i32.eq - drop - local.get 5 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 5 - i32.store offset=12 - br 1 (;@3;) - end - local.get 7 - i32.load offset=24 - local.set 12 - block ;; label = @4 - block ;; label = @5 - local.get 7 - i32.load offset=12 - local.tee 8 - local.get 7 - i32.eq - br_if 0 (;@5;) - local.get 7 - i32.load offset=8 - local.tee 1 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 8 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 8 - i32.store offset=12 - br 1 (;@4;) - end - block ;; label = @5 - local.get 7 - i32.const 20 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - local.get 7 - i32.const 16 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - i32.const 0 - local.set 8 - br 1 (;@4;) - end - loop ;; label = @5 - local.get 1 - local.set 11 - local.get 5 - local.tee 8 - i32.const 20 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - local.get 8 - i32.const 16 - i32.add - local.set 1 - local.get 8 - i32.load offset=16 - local.tee 5 - br_if 0 (;@5;) - end - local.get 11 - i32.const 0 - i32.store - end - local.get 12 - i32.eqz - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 7 - local.get 7 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 1 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 1 - local.get 8 - i32.store - local.get 8 - br_if 1 (;@4;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@3;) - end - local.get 12 - i32.const 16 - i32.const 20 - local.get 12 - i32.load offset=16 - local.get 7 - i32.eq - select - i32.add - local.get 8 - i32.store - local.get 8 - i32.eqz - br_if 1 (;@3;) - end - local.get 8 - local.get 12 - i32.store offset=24 - block ;; label = @4 - local.get 7 - i32.load offset=16 - local.tee 1 - i32.eqz - br_if 0 (;@4;) - local.get 8 - local.get 1 - i32.store offset=16 - local.get 1 - local.get 8 - i32.store offset=24 - end - local.get 7 - i32.load offset=20 - local.tee 1 - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const 20 - i32.add - local.get 1 - i32.store - local.get 1 - local.get 8 - i32.store offset=24 - end - block ;; label = @3 - local.get 10 - i32.const 15 - i32.gt_u - br_if 0 (;@3;) - local.get 3 - local.get 4 - i32.const 1 - i32.and - local.get 9 - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 9 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 1 - local.get 10 - i32.const 3 - i32.or - i32.store offset=4 - local.get 6 - local.get 9 - i32.add - local.tee 2 - local.get 2 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 10 - call $dispose_chunk - local.get 0 - return - end - block ;; label = @2 - local.get 1 - call $dlmalloc - local.tee 2 - br_if 0 (;@2;) - i32.const 0 - return - end - local.get 2 - local.get 0 - i32.const -4 - i32.const -8 - local.get 3 - i32.load - local.tee 5 - i32.const 3 - i32.and - select - local.get 5 - i32.const -8 - i32.and - i32.add - local.tee 5 - local.get 1 - local.get 5 - local.get 1 - i32.lt_u - select - call $memcpy - local.set 1 - local.get 0 - call $dlfree - local.get 1 - local.set 0 - end - local.get 0 - ) - (func $dispose_chunk (;22;) (type 5) (param i32 i32) - (local i32 i32 i32 i32 i32 i32) - local.get 0 - local.get 1 - i32.add - local.set 2 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 1 - i32.and - br_if 0 (;@2;) - local.get 3 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 0 - i32.load - local.tee 3 - local.get 1 - i32.add - local.set 1 - block ;; label = @3 - block ;; label = @4 - local.get 0 - local.get 3 - i32.sub - local.tee 0 - i32.const 0 - i32.load offset=1048600 - i32.eq - br_if 0 (;@4;) - block ;; label = @5 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 0 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - local.get 0 - i32.load offset=12 - local.tee 3 - local.get 4 - i32.ne - br_if 2 (;@3;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 3 (;@2;) - end - local.get 0 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 0 - i32.load offset=12 - local.tee 6 - local.get 0 - i32.eq - br_if 0 (;@6;) - local.get 0 - i32.load offset=8 - local.tee 3 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 0 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 0 - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 3 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 3 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 2 (;@2;) - block ;; label = @5 - block ;; label = @6 - local.get 0 - local.get 0 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 4 (;@2;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 0 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 3 (;@2;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 0 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 0 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 2 (;@2;) - local.get 6 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - br 2 (;@2;) - end - local.get 2 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 1 (;@2;) - local.get 2 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - i32.const 0 - local.get 1 - i32.store offset=1048588 - local.get 2 - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - return - end - local.get 3 - local.get 6 - i32.eq - drop - local.get 3 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 3 - i32.store offset=12 - end - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.load offset=4 - local.tee 3 - i32.const 2 - i32.and - br_if 0 (;@3;) - block ;; label = @4 - local.get 2 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 0 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 1 - i32.add - local.tee 1 - i32.store offset=1048592 - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 3 (;@1;) - i32.const 0 - i32.const 0 - i32.store offset=1048588 - i32.const 0 - i32.const 0 - i32.store offset=1048600 - return - end - block ;; label = @4 - local.get 2 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 0 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 1 - i32.add - local.tee 1 - i32.store offset=1048588 - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - return - end - local.get 3 - i32.const -8 - i32.and - local.get 1 - i32.add - local.set 1 - block ;; label = @4 - block ;; label = @5 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 2 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @6 - local.get 2 - i32.load offset=12 - local.tee 3 - local.get 4 - i32.ne - br_if 0 (;@6;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@4;) - end - local.get 3 - local.get 6 - i32.eq - drop - local.get 3 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 3 - i32.store offset=12 - br 1 (;@4;) - end - local.get 2 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 2 - i32.load offset=12 - local.tee 6 - local.get 2 - i32.eq - br_if 0 (;@6;) - local.get 2 - i32.load offset=8 - local.tee 3 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 2 - i32.const 20 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - local.get 2 - i32.const 16 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 4 - local.set 5 - local.get 3 - local.tee 6 - i32.const 20 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 4 - local.get 6 - i32.load offset=16 - local.tee 3 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 0 (;@4;) - block ;; label = @5 - block ;; label = @6 - local.get 2 - local.get 2 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@4;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 2 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 1 (;@4;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 2 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 2 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 0 (;@4;) - local.get 6 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 1 (;@2;) - i32.const 0 - local.get 1 - i32.store offset=1048588 - return - end - local.get 2 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - end - block ;; label = @2 - local.get 1 - i32.const 255 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 3 - block ;; label = @3 - block ;; label = @4 - i32.const 0 - i32.load offset=1048580 - local.tee 4 - i32.const 1 - local.get 1 - i32.const 3 - i32.shr_u - i32.shl - local.tee 1 - i32.and - br_if 0 (;@4;) - i32.const 0 - local.get 4 - local.get 1 - i32.or - i32.store offset=1048580 - local.get 3 - local.set 1 - br 1 (;@3;) - end - local.get 3 - i32.load offset=8 - local.set 1 - end - local.get 1 - local.get 0 - i32.store offset=12 - local.get 3 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 3 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - return - end - i32.const 31 - local.set 3 - block ;; label = @2 - local.get 1 - i32.const 16777215 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.const 38 - local.get 1 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 0 - local.get 3 - i32.store offset=28 - local.get 0 - i64.const 0 - i64.store offset=16 align=4 - local.get 3 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 4 - block ;; label = @2 - i32.const 0 - i32.load offset=1048584 - local.tee 6 - i32.const 1 - local.get 3 - i32.shl - local.tee 2 - i32.and - br_if 0 (;@2;) - local.get 4 - local.get 0 - i32.store - i32.const 0 - local.get 6 - local.get 2 - i32.or - i32.store offset=1048584 - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 0 - i32.store offset=12 - return - end - local.get 1 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 4 - i32.load - local.set 6 - block ;; label = @2 - loop ;; label = @3 - local.get 6 - local.tee 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 1 - i32.eq - br_if 1 (;@2;) - local.get 3 - i32.const 29 - i32.shr_u - local.set 6 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 4 - local.get 6 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 6 - br_if 0 (;@3;) - end - local.get 2 - local.get 0 - i32.store - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=12 - local.get 0 - local.get 0 - i32.store offset=8 - return - end - local.get 4 - i32.load offset=8 - local.tee 1 - local.get 0 - i32.store offset=12 - local.get 4 - local.get 0 - i32.store offset=8 - local.get 0 - i32.const 0 - i32.store offset=24 - local.get 0 - local.get 4 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - end - ) - (func $internal_memalign (;23;) (type 6) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.const 16 - local.get 0 - i32.const 16 - i32.gt_u - select - local.tee 2 - local.get 2 - i32.const -1 - i32.add - i32.and - br_if 0 (;@2;) - local.get 2 - local.set 0 - br 1 (;@1;) - end - i32.const 32 - local.set 3 - loop ;; label = @2 - local.get 3 - local.tee 0 - i32.const 1 - i32.shl - local.set 3 - local.get 0 - local.get 2 - i32.lt_u - br_if 0 (;@2;) - end - end - block ;; label = @1 - i32.const -64 - local.get 0 - i32.sub - local.get 1 - i32.gt_u - br_if 0 (;@1;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const 0 - return - end - block ;; label = @1 - local.get 0 - i32.const 16 - local.get 1 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.tee 1 - i32.add - i32.const 12 - i32.add - call $dlmalloc - local.tee 3 - br_if 0 (;@1;) - i32.const 0 - return - end - local.get 3 - i32.const -8 - i32.add - local.set 2 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.const -1 - i32.add - local.get 3 - i32.and - br_if 0 (;@2;) - local.get 2 - local.set 0 - br 1 (;@1;) - end - local.get 3 - i32.const -4 - i32.add - local.tee 4 - i32.load - local.tee 5 - i32.const -8 - i32.and - local.get 3 - local.get 0 - i32.add - i32.const -1 - i32.add - i32.const 0 - local.get 0 - i32.sub - i32.and - i32.const -8 - i32.add - local.tee 3 - i32.const 0 - local.get 0 - local.get 3 - local.get 2 - i32.sub - i32.const 15 - i32.gt_u - select - i32.add - local.tee 0 - local.get 2 - i32.sub - local.tee 3 - i32.sub - local.set 6 - block ;; label = @2 - local.get 5 - i32.const 3 - i32.and - br_if 0 (;@2;) - local.get 0 - local.get 6 - i32.store offset=4 - local.get 0 - local.get 2 - i32.load - local.get 3 - i32.add - i32.store - br 1 (;@1;) - end - local.get 0 - local.get 6 - local.get 0 - i32.load offset=4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 0 - local.get 6 - i32.add - local.tee 6 - local.get 6 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - local.get 4 - i32.load - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 2 - local.get 3 - i32.add - local.tee 6 - local.get 6 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 3 - call $dispose_chunk - end - block ;; label = @1 - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@1;) - local.get 3 - i32.const -8 - i32.and - local.tee 2 - local.get 1 - i32.const 16 - i32.add - i32.le_u - br_if 0 (;@1;) - local.get 0 - local.get 1 - local.get 3 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.tee 3 - local.get 2 - local.get 1 - i32.sub - local.tee 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 2 - i32.add - local.tee 2 - local.get 2 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 1 - call $dispose_chunk - end - local.get 0 - i32.const 8 - i32.add - ) - (func $aligned_alloc (;24;) (type 6) (param i32 i32) (result i32) - block ;; label = @1 - local.get 0 - i32.const 16 - i32.gt_u - br_if 0 (;@1;) - local.get 1 - call $dlmalloc - return - end - local.get 0 - local.get 1 - call $internal_memalign - ) - (func $abort (;25;) (type 3) - unreachable - unreachable - ) - (func $sbrk (;26;) (type 9) (param i32) (result i32) - block ;; label = @1 - local.get 0 - br_if 0 (;@1;) - memory.size - i32.const 16 - i32.shl - return - end - block ;; label = @1 - local.get 0 - i32.const 65535 - i32.and - br_if 0 (;@1;) - local.get 0 - i32.const -1 - i32.le_s - br_if 0 (;@1;) - block ;; label = @2 - local.get 0 - i32.const 16 - i32.shr_u - memory.grow - local.tee 0 - i32.const -1 - i32.ne - br_if 0 (;@2;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const -1 - return - end - local.get 0 - i32.const 16 - i32.shl - return - end - call $abort - unreachable - ) - (func $memcpy (;27;) (type 10) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 32 - i32.gt_u - br_if 0 (;@3;) - local.get 1 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@2;) - local.get 2 - i32.eqz - br_if 1 (;@2;) - local.get 0 - local.get 1 - i32.load8_u - i32.store8 - local.get 2 - i32.const -1 - i32.add - local.set 3 - local.get 0 - i32.const 1 - i32.add - local.set 4 - local.get 1 - i32.const 1 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=1 - i32.store8 offset=1 - local.get 2 - i32.const -2 - i32.add - local.set 3 - local.get 0 - i32.const 2 - i32.add - local.set 4 - local.get 1 - i32.const 2 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=2 - i32.store8 offset=2 - local.get 2 - i32.const -3 - i32.add - local.set 3 - local.get 0 - i32.const 3 - i32.add - local.set 4 - local.get 1 - i32.const 3 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=3 - i32.store8 offset=3 - local.get 2 - i32.const -4 - i32.add - local.set 3 - local.get 0 - i32.const 4 - i32.add - local.set 4 - local.get 1 - i32.const 4 - i32.add - local.set 5 - br 2 (;@1;) - end - local.get 0 - local.get 1 - local.get 2 - memory.copy - local.get 0 - return - end - local.get 2 - local.set 3 - local.get 0 - local.set 4 - local.get 1 - local.set 5 - end - block ;; label = @1 - block ;; label = @2 - local.get 4 - i32.const 3 - i32.and - local.tee 2 - br_if 0 (;@2;) - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 16 - i32.ge_u - br_if 0 (;@4;) - local.get 3 - local.set 2 - br 1 (;@3;) - end - block ;; label = @4 - local.get 3 - i32.const -16 - i32.add - local.tee 2 - i32.const 16 - i32.and - br_if 0 (;@4;) - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 4 - local.get 5 - i64.load offset=8 align=4 - i64.store offset=8 align=4 - local.get 4 - i32.const 16 - i32.add - local.set 4 - local.get 5 - i32.const 16 - i32.add - local.set 5 - local.get 2 - local.set 3 - end - local.get 2 - i32.const 16 - i32.lt_u - br_if 0 (;@3;) - local.get 3 - local.set 2 - loop ;; label = @4 - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 4 - local.get 5 - i64.load offset=8 align=4 - i64.store offset=8 align=4 - local.get 4 - local.get 5 - i64.load offset=16 align=4 - i64.store offset=16 align=4 - local.get 4 - local.get 5 - i64.load offset=24 align=4 - i64.store offset=24 align=4 - local.get 4 - i32.const 32 - i32.add - local.set 4 - local.get 5 - i32.const 32 - i32.add - local.set 5 - local.get 2 - i32.const -32 - i32.add - local.tee 2 - i32.const 15 - i32.gt_u - br_if 0 (;@4;) - end - end - block ;; label = @3 - local.get 2 - i32.const 8 - i32.lt_u - br_if 0 (;@3;) - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 5 - i32.const 8 - i32.add - local.set 5 - local.get 4 - i32.const 8 - i32.add - local.set 4 - end - block ;; label = @3 - local.get 2 - i32.const 4 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 4 - local.get 5 - i32.load - i32.store - local.get 5 - i32.const 4 - i32.add - local.set 5 - local.get 4 - i32.const 4 - i32.add - local.set 4 - end - block ;; label = @3 - local.get 2 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 4 - local.get 5 - i32.load16_u align=1 - i32.store16 align=1 - local.get 4 - i32.const 2 - i32.add - local.set 4 - local.get 5 - i32.const 2 - i32.add - local.set 5 - end - local.get 2 - i32.const 1 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 4 - local.get 5 - i32.load8_u - i32.store8 - local.get 0 - return - end - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.const 32 - i32.lt_u - br_if 0 (;@6;) - block ;; label = @7 - block ;; label = @8 - local.get 2 - i32.const -1 - i32.add - br_table 3 (;@5;) 0 (;@8;) 1 (;@7;) 7 (;@1;) - end - local.get 4 - local.get 5 - i32.load - i32.store16 align=1 - local.get 4 - local.get 5 - i32.const 2 - i32.add - i32.load align=2 - i32.store offset=2 - local.get 4 - local.get 5 - i32.const 6 - i32.add - i64.load align=2 - i64.store offset=6 align=4 - local.get 4 - i32.const 18 - i32.add - local.set 2 - local.get 5 - i32.const 18 - i32.add - local.set 1 - i32.const 14 - local.set 6 - local.get 5 - i32.const 14 - i32.add - i32.load align=2 - local.set 5 - i32.const 14 - local.set 3 - br 3 (;@4;) - end - local.get 4 - local.get 5 - i32.load - i32.store8 - local.get 4 - local.get 5 - i32.const 1 - i32.add - i32.load align=1 - i32.store offset=1 - local.get 4 - local.get 5 - i32.const 5 - i32.add - i64.load align=1 - i64.store offset=5 align=4 - local.get 4 - i32.const 17 - i32.add - local.set 2 - local.get 5 - i32.const 17 - i32.add - local.set 1 - i32.const 13 - local.set 6 - local.get 5 - i32.const 13 - i32.add - i32.load align=1 - local.set 5 - i32.const 15 - local.set 3 - br 2 (;@4;) - end - block ;; label = @6 - block ;; label = @7 - local.get 3 - i32.const 16 - i32.ge_u - br_if 0 (;@7;) - local.get 4 - local.set 2 - local.get 5 - local.set 1 - br 1 (;@6;) - end - local.get 4 - local.get 5 - i32.load8_u - i32.store8 - local.get 4 - local.get 5 - i32.load offset=1 align=1 - i32.store offset=1 align=1 - local.get 4 - local.get 5 - i64.load offset=5 align=1 - i64.store offset=5 align=1 - local.get 4 - local.get 5 - i32.load16_u offset=13 align=1 - i32.store16 offset=13 align=1 - local.get 4 - local.get 5 - i32.load8_u offset=15 - i32.store8 offset=15 - local.get 4 - i32.const 16 - i32.add - local.set 2 - local.get 5 - i32.const 16 - i32.add - local.set 1 - end - local.get 3 - i32.const 8 - i32.and - br_if 2 (;@3;) - br 3 (;@2;) - end - local.get 4 - local.get 5 - i32.load - local.tee 2 - i32.store8 - local.get 4 - local.get 2 - i32.const 16 - i32.shr_u - i32.store8 offset=2 - local.get 4 - local.get 2 - i32.const 8 - i32.shr_u - i32.store8 offset=1 - local.get 4 - local.get 5 - i32.const 3 - i32.add - i32.load align=1 - i32.store offset=3 - local.get 4 - local.get 5 - i32.const 7 - i32.add - i64.load align=1 - i64.store offset=7 align=4 - local.get 4 - i32.const 19 - i32.add - local.set 2 - local.get 5 - i32.const 19 - i32.add - local.set 1 - i32.const 15 - local.set 6 - local.get 5 - i32.const 15 - i32.add - i32.load align=1 - local.set 5 - i32.const 13 - local.set 3 - end - local.get 4 - local.get 6 - i32.add - local.get 5 - i32.store - end - local.get 2 - local.get 1 - i64.load align=1 - i64.store align=1 - local.get 2 - i32.const 8 - i32.add - local.set 2 - local.get 1 - i32.const 8 - i32.add - local.set 1 - end - block ;; label = @2 - local.get 3 - i32.const 4 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - local.get 1 - i32.load align=1 - i32.store align=1 - local.get 2 - i32.const 4 - i32.add - local.set 2 - local.get 1 - i32.const 4 - i32.add - local.set 1 - end - block ;; label = @2 - local.get 3 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - local.get 1 - i32.load16_u align=1 - i32.store16 align=1 - local.get 2 - i32.const 2 - i32.add - local.set 2 - local.get 1 - i32.const 2 - i32.add - local.set 1 - end - local.get 3 - i32.const 1 - i32.and - i32.eqz - br_if 0 (;@1;) - local.get 2 - local.get 1 - i32.load8_u - i32.store8 - end - local.get 0 - ) - (func $alloc::alloc::handle_alloc_error (;28;) (type 5) (param i32 i32) - unreachable - unreachable - ) - (func $alloc::raw_vec::capacity_overflow (;29;) (type 3) - unreachable - unreachable - ) - (table (;0;) 1 1 funcref) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (export "memory" (memory 0)) - (export "miden:base/note@1.0.0#note-script" (func $miden:base/note@1.0.0#note-script)) - (export "cabi_realloc" (func $cabi_realloc)) - ) - (core module (;1;) - (type (;0;) (func (param i32))) - (func $indirect-miden:base/tx-kernel@1.0.0-get-inputs (;0;) (type 0) (param i32) - local.get 0 - i32.const 0 - call_indirect (type 0) - ) - (func $indirect-miden:base/tx-kernel@1.0.0-get-assets (;1;) (type 0) (param i32) - local.get 0 - i32.const 1 - call_indirect (type 0) - ) - (table (;0;) 2 2 funcref) - (export "0" (func $indirect-miden:base/tx-kernel@1.0.0-get-inputs)) - (export "1" (func $indirect-miden:base/tx-kernel@1.0.0-get-assets)) - (export "$imports" (table 0)) - ) - (core module (;2;) - (type (;0;) (func (param i32))) - (import "" "0" (func (;0;) (type 0))) - (import "" "1" (func (;1;) (type 0))) - (import "" "$imports" (table (;0;) 2 2 funcref)) - (elem (;0;) (i32.const 0) func 0 1) - ) - (core instance (;0;) (instantiate 1)) - (alias export 1 "get-id" (func (;0;))) - (core func (;0;) (canon lower (func 0))) - (alias core export 0 "0" (core func (;1;))) - (alias core export 0 "1" (core func (;2;))) - (core instance (;1;) - (export "get-id" (func 0)) - (export "get-inputs" (func 1)) - (export "get-assets" (func 2)) - ) - (alias export 2 "receive-asset" (func (;1;))) - (core func (;3;) (canon lower (func 1))) - (core instance (;2;) - (export "receive-asset" (func 3)) - ) - (core instance (;3;) (instantiate 0 - (with "miden:base/tx-kernel@1.0.0" (instance 1)) - (with "miden:basic-wallet/basic-wallet@1.0.0" (instance 2)) - ) - ) - (alias core export 3 "memory" (core memory (;0;))) - (alias core export 3 "cabi_realloc" (core func (;4;))) - (alias core export 0 "$imports" (core table (;0;))) - (alias export 1 "get-inputs" (func (;2;))) - (core func (;5;) (canon lower (func 2) (memory 0) (realloc 4))) - (alias export 1 "get-assets" (func (;3;))) - (core func (;6;) (canon lower (func 3) (memory 0) (realloc 4))) - (core instance (;4;) - (export "$imports" (table 0)) - (export "0" (func 5)) - (export "1" (func 6)) - ) - (core instance (;5;) (instantiate 2 - (with "" (instance 4)) - ) - ) - (type (;7;) (func)) - (alias core export 3 "miden:base/note@1.0.0#note-script" (core func (;7;))) - (func (;4;) (type 7) (canon lift (core func 7))) - (component (;0;) - (type (;0;) (func)) - (import "import-func-note-script" (func (;0;) (type 0))) - (type (;1;) (func)) - (export (;1;) "note-script" (func 0) (func (type 1))) - ) - (instance (;3;) (instantiate 0 - (with "import-func-note-script" (func 4)) - ) - ) - (export (;4;) "miden:base/note@1.0.0" (instance 3)) -) \ No newline at end of file diff --git a/tests/integration/expected/sdk_basic_wallet/bindings/basic_wallet_bindings.rs b/tests/integration/expected/sdk_basic_wallet/bindings/basic_wallet_bindings.rs deleted file mode 100644 index cc6a9bcae..000000000 --- a/tests/integration/expected/sdk_basic_wallet/bindings/basic_wallet_bindings.rs +++ /dev/null @@ -1,549 +0,0 @@ -// Generated by `wit-bindgen` 0.16.0. DO NOT EDIT! -pub mod miden { - pub mod base { - - #[allow(clippy::all)] - pub mod types { - #[used] - #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = super::super::super::__link_section; - /// Represents base field element in the field using Montgomery representation. - /// Internal values represent x * R mod M where R = 2^64 mod M and x in [0, M). - /// The backing type is `u64` but the internal values are always in the range [0, M). - /// Field modulus M = 2^64 - 2^32 + 1 - pub type Felt = u64; - /// A group of four field elements in the Miden base field. - pub type Word = (Felt,Felt,Felt,Felt,); - /// Unique identifier of an account. - /// - /// Account ID consists of 1 field element (~64 bits). This field element uniquely identifies a - /// single account and also specifies the type of the underlying account. Specifically: - /// - The two most significant bits of the ID specify the type of the account: - /// - 00 - regular account with updatable code. - /// - 01 - regular account with immutable code. - /// - 10 - fungible asset faucet with immutable code. - /// - 11 - non-fungible asset faucet with immutable code. - /// - The third most significant bit of the ID specifies whether the account data is stored on-chain: - /// - 0 - full account data is stored on-chain. - /// - 1 - only the account hash is stored on-chain which serves as a commitment to the account state. - /// As such the three most significant bits fully describes the type of the account. - pub type AccountId = Felt; - /// Recipient of the note, i.e., hash(hash(hash(serial_num, [0; 4]), note_script_hash), input_hash) - pub type Recipient = Word; - pub type Tag = Felt; - /// A fungible asset - #[repr(C)] - #[derive(Clone, Copy)] - pub struct FungibleAsset { - /// Faucet ID of the faucet which issued the asset as well as the asset amount. - pub asset: AccountId, - /// Asset amount is guaranteed to be 2^63 - 1 or smaller. - pub amount: u64, - } - impl ::core::fmt::Debug for FungibleAsset { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - f.debug_struct("FungibleAsset").field("asset", &self.asset).field("amount", &self.amount).finish() - } - } - /// A commitment to a non-fungible asset. - /// - /// A non-fungible asset consists of 4 field elements which are computed by hashing asset data - /// (which can be of arbitrary length) to produce: [d0, d1, d2, d3]. We then replace d1 with the - /// faucet_id that issued the asset: [d0, faucet_id, d2, d3]. We then set the most significant bit - /// of the most significant element to ZERO. - pub type NonFungibleAsset = Word; - /// A fungible or a non-fungible asset. - /// - /// All assets are encoded using a single word (4 elements) such that it is easy to determine the - /// type of an asset both inside and outside Miden VM. Specifically: - /// Element 1 will be: - /// - ZERO for a fungible asset - /// - non-ZERO for a non-fungible asset - /// The most significant bit will be: - /// - ONE for a fungible asset - /// - ZERO for a non-fungible asset - /// - /// The above properties guarantee that there can never be a collision between a fungible and a - /// non-fungible asset. - /// - /// The methodology for constructing fungible and non-fungible assets is described below. - /// - /// # Fungible assets - /// The most significant element of a fungible asset is set to the ID of the faucet which issued - /// the asset. This guarantees the properties described above (the first bit is ONE). - /// - /// The least significant element is set to the amount of the asset. This amount cannot be greater - /// than 2^63 - 1 and thus requires 63-bits to store. - /// - /// Elements 1 and 2 are set to ZERO. - /// - /// It is impossible to find a collision between two fungible assets issued by different faucets as - /// the faucet_id is included in the description of the asset and this is guaranteed to be different - /// for each faucet as per the faucet creation logic. - /// - /// # Non-fungible assets - /// The 4 elements of non-fungible assets are computed as follows: - /// - First the asset data is hashed. This compresses an asset of an arbitrary length to 4 field - /// elements: [d0, d1, d2, d3]. - /// - d1 is then replaced with the faucet_id which issues the asset: [d0, faucet_id, d2, d3]. - /// - Lastly, the most significant bit of d3 is set to ZERO. - /// - /// It is impossible to find a collision between two non-fungible assets issued by different faucets - /// as the faucet_id is included in the description of the non-fungible asset and this is guaranteed - /// to be different as per the faucet creation logic. Collision resistance for non-fungible assets - /// issued by the same faucet is ~2^95. - #[derive(Clone, Copy)] - pub enum Asset{ - Fungible(FungibleAsset), - NonFungible(NonFungibleAsset), - } - impl ::core::fmt::Debug for Asset { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - match self { - Asset::Fungible(e) => { - f.debug_tuple("Asset::Fungible").field(e).finish() - } - Asset::NonFungible(e) => { - f.debug_tuple("Asset::NonFungible").field(e).finish() - } - } - } - } - /// Inputs of the currently executed note, never exceeds 16 felts - pub type NoteInputs = ::cargo_component_bindings::rt::vec::Vec::; - - } - - - #[allow(clippy::all)] - pub mod tx_kernel { - #[used] - #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = super::super::super::__link_section; - pub type Asset = super::super::super::miden::base::types::Asset; - pub type Tag = super::super::super::miden::base::types::Tag; - pub type Recipient = super::super::super::miden::base::types::Recipient; - pub type NoteInputs = super::super::super::miden::base::types::NoteInputs; - pub type AccountId = super::super::super::miden::base::types::AccountId; - #[allow(unused_unsafe, clippy::all)] - /// Account-related functions - /// Get the id of the currently executing account - pub fn get_id() -> AccountId{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "get-id"] - fn wit_import() -> i64; - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import() -> i64{ unreachable!() } - let ret = wit_import(); - ret as u64 - } - } - #[allow(unused_unsafe, clippy::all)] - /// Add the specified asset to the vault - pub fn add_asset(asset: Asset,) -> Asset{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[repr(align(8))] - struct RetArea([u8; 40]); - let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - use super::super::super::miden::base::types::Asset as V2; - let (result3_0,result3_1,result3_2,result3_3,result3_4,) = match asset { - V2::Fungible(e) => { - let super::super::super::miden::base::types::FungibleAsset{ asset:asset0, amount:amount0, } = e; - - (0i32, ::cargo_component_bindings::rt::as_i64(asset0), ::cargo_component_bindings::rt::as_i64(amount0), 0i64, 0i64) - }, - V2::NonFungible(e) => { - let (t1_0, t1_1, t1_2, t1_3, ) = e; - - (1i32, ::cargo_component_bindings::rt::as_i64(t1_0), ::cargo_component_bindings::rt::as_i64(t1_1), ::cargo_component_bindings::rt::as_i64(t1_2), ::cargo_component_bindings::rt::as_i64(t1_3)) - }, - }; - let ptr4 = ret_area.as_mut_ptr() as i32; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "add-asset"] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i32, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i32, ){ unreachable!() } - wit_import(result3_0, result3_1, result3_2, result3_3, result3_4, ptr4); - let l5 = i32::from(*((ptr4 + 0) as *const u8)); - use super::super::super::miden::base::types::Asset as V12; - let v12 = match l5 { - 0 => { - let e12 = { - let l6 = *((ptr4 + 8) as *const i64); - let l7 = *((ptr4 + 16) as *const i64); - - super::super::super::miden::base::types::FungibleAsset{ - asset: l6 as u64, - amount: l7 as u64, - } - }; - V12::Fungible(e12) - } - n => { - debug_assert_eq!(n, 1, "invalid enum discriminant"); - let e12 = { - let l8 = *((ptr4 + 8) as *const i64); - let l9 = *((ptr4 + 16) as *const i64); - let l10 = *((ptr4 + 24) as *const i64); - let l11 = *((ptr4 + 32) as *const i64); - - (l8 as u64, l9 as u64, l10 as u64, l11 as u64) - }; - V12::NonFungible(e12) - } - }; - v12 - } - } - #[allow(unused_unsafe, clippy::all)] - /// Remove the specified asset from the vault - pub fn remove_asset(asset: Asset,) -> Asset{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[repr(align(8))] - struct RetArea([u8; 40]); - let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - use super::super::super::miden::base::types::Asset as V2; - let (result3_0,result3_1,result3_2,result3_3,result3_4,) = match asset { - V2::Fungible(e) => { - let super::super::super::miden::base::types::FungibleAsset{ asset:asset0, amount:amount0, } = e; - - (0i32, ::cargo_component_bindings::rt::as_i64(asset0), ::cargo_component_bindings::rt::as_i64(amount0), 0i64, 0i64) - }, - V2::NonFungible(e) => { - let (t1_0, t1_1, t1_2, t1_3, ) = e; - - (1i32, ::cargo_component_bindings::rt::as_i64(t1_0), ::cargo_component_bindings::rt::as_i64(t1_1), ::cargo_component_bindings::rt::as_i64(t1_2), ::cargo_component_bindings::rt::as_i64(t1_3)) - }, - }; - let ptr4 = ret_area.as_mut_ptr() as i32; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "remove-asset"] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i32, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i32, ){ unreachable!() } - wit_import(result3_0, result3_1, result3_2, result3_3, result3_4, ptr4); - let l5 = i32::from(*((ptr4 + 0) as *const u8)); - use super::super::super::miden::base::types::Asset as V12; - let v12 = match l5 { - 0 => { - let e12 = { - let l6 = *((ptr4 + 8) as *const i64); - let l7 = *((ptr4 + 16) as *const i64); - - super::super::super::miden::base::types::FungibleAsset{ - asset: l6 as u64, - amount: l7 as u64, - } - }; - V12::Fungible(e12) - } - n => { - debug_assert_eq!(n, 1, "invalid enum discriminant"); - let e12 = { - let l8 = *((ptr4 + 8) as *const i64); - let l9 = *((ptr4 + 16) as *const i64); - let l10 = *((ptr4 + 24) as *const i64); - let l11 = *((ptr4 + 32) as *const i64); - - (l8 as u64, l9 as u64, l10 as u64, l11 as u64) - }; - V12::NonFungible(e12) - } - }; - v12 - } - } - #[allow(unused_unsafe, clippy::all)] - /// Note-related functions - /// Creates a new note. - /// asset is the asset to be included in the note. - /// tag is the tag to be included in the note. - /// recipient is the recipient of the note. - pub fn create_note(asset: Asset,tag: Tag,recipient: Recipient,){ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - use super::super::super::miden::base::types::Asset as V2; - let (result3_0,result3_1,result3_2,result3_3,result3_4,) = match asset { - V2::Fungible(e) => { - let super::super::super::miden::base::types::FungibleAsset{ asset:asset0, amount:amount0, } = e; - - (0i32, ::cargo_component_bindings::rt::as_i64(asset0), ::cargo_component_bindings::rt::as_i64(amount0), 0i64, 0i64) - }, - V2::NonFungible(e) => { - let (t1_0, t1_1, t1_2, t1_3, ) = e; - - (1i32, ::cargo_component_bindings::rt::as_i64(t1_0), ::cargo_component_bindings::rt::as_i64(t1_1), ::cargo_component_bindings::rt::as_i64(t1_2), ::cargo_component_bindings::rt::as_i64(t1_3)) - }, - }; - let (t4_0, t4_1, t4_2, t4_3, ) = recipient; - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "create-note"] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, ){ unreachable!() } - wit_import(result3_0, result3_1, result3_2, result3_3, result3_4, ::cargo_component_bindings::rt::as_i64(tag), ::cargo_component_bindings::rt::as_i64(t4_0), ::cargo_component_bindings::rt::as_i64(t4_1), ::cargo_component_bindings::rt::as_i64(t4_2), ::cargo_component_bindings::rt::as_i64(t4_3)); - } - } - #[allow(unused_unsafe, clippy::all)] - /// Get the inputs of the currently executed note - pub fn get_inputs() -> NoteInputs{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[repr(align(4))] - struct RetArea([u8; 8]); - let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - let ptr0 = ret_area.as_mut_ptr() as i32; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "get-inputs"] - fn wit_import(_: i32, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, ){ unreachable!() } - wit_import(ptr0); - let l1 = *((ptr0 + 0) as *const i32); - let l2 = *((ptr0 + 4) as *const i32); - let len3 = l2 as usize; - Vec::from_raw_parts(l1 as *mut _, len3, len3) - } - } - #[allow(unused_unsafe, clippy::all)] - /// Get the assets of the currently executing note - pub fn get_assets() -> ::cargo_component_bindings::rt::vec::Vec::{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[repr(align(4))] - struct RetArea([u8; 8]); - let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - let ptr0 = ret_area.as_mut_ptr() as i32; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "get-assets"] - fn wit_import(_: i32, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, ){ unreachable!() } - wit_import(ptr0); - let l1 = *((ptr0 + 0) as *const i32); - let l2 = *((ptr0 + 4) as *const i32); - let base11 = l1; - let len11 = l2; - let mut result11 = Vec::with_capacity(len11 as usize); - for i in 0..len11 { - let base = base11 + i * 40; - let e11 = { - let l3 = i32::from(*((base + 0) as *const u8)); - use super::super::super::miden::base::types::Asset as V10; - let v10 = match l3 { - 0 => { - let e10 = { - let l4 = *((base + 8) as *const i64); - let l5 = *((base + 16) as *const i64); - - super::super::super::miden::base::types::FungibleAsset{ - asset: l4 as u64, - amount: l5 as u64, - } - }; - V10::Fungible(e10) - } - n => { - debug_assert_eq!(n, 1, "invalid enum discriminant"); - let e10 = { - let l6 = *((base + 8) as *const i64); - let l7 = *((base + 16) as *const i64); - let l8 = *((base + 24) as *const i64); - let l9 = *((base + 32) as *const i64); - - (l6 as u64, l7 as u64, l8 as u64, l9 as u64) - }; - V10::NonFungible(e10) - } - }; - - v10 - }; - result11.push(e11); - } - ::cargo_component_bindings::rt::dealloc(base11, (len11 as usize) * 40, 8); - result11 - } - } - - } - - } -} -pub mod exports { - pub mod miden { - pub mod base { - - #[allow(clippy::all)] - pub mod account { - #[used] - #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_section; - - } - - } - pub mod basic_wallet { - - #[allow(clippy::all)] - pub mod basic_wallet { - #[used] - #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_section; - pub type Asset = super::super::super::super::miden::base::types::Asset; - pub type Tag = super::super::super::super::miden::base::types::Tag; - pub type Recipient = super::super::super::super::miden::base::types::Recipient; - const _: () = { - - #[doc(hidden)] - #[export_name = "miden:basic-wallet/basic-wallet@1.0.0#receive-asset"] - #[allow(non_snake_case)] - unsafe extern "C" fn __export_receive_asset(arg0: i32,arg1: i64,arg2: i64,arg3: i64,arg4: i64,) { - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - - // Before executing any other code, use this function to run all static - // constructors, if they have not yet been run. This is a hack required - // to work around wasi-libc ctors calling import functions to initialize - // the environment. - // - // This functionality will be removed once rust 1.69.0 is stable, at which - // point wasi-libc will no longer have this behavior. - // - // See - // https://github.com/bytecodealliance/preview2-prototyping/issues/99 - // for more details. - #[cfg(target_arch="wasm32")] - ::cargo_component_bindings::rt::run_ctors_once(); - - use super::super::super::super::miden::base::types::Asset as V0; - let v0 = match arg0 { - 0 => { - let e0 = super::super::super::super::miden::base::types::FungibleAsset{ - asset: arg1 as u64, - amount: arg2 as u64, - }; - V0::Fungible(e0) - } - n => { - debug_assert_eq!(n, 1, "invalid enum discriminant"); - let e0 = (arg1 as u64, arg2 as u64, arg3 as u64, arg4 as u64); - V0::NonFungible(e0) - } - }; - <_GuestImpl as Guest>::receive_asset(v0); - } - }; - const _: () = { - - #[doc(hidden)] - #[export_name = "miden:basic-wallet/basic-wallet@1.0.0#send-asset"] - #[allow(non_snake_case)] - unsafe extern "C" fn __export_send_asset(arg0: i32,arg1: i64,arg2: i64,arg3: i64,arg4: i64,arg5: i64,arg6: i64,arg7: i64,arg8: i64,arg9: i64,) { - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - - // Before executing any other code, use this function to run all static - // constructors, if they have not yet been run. This is a hack required - // to work around wasi-libc ctors calling import functions to initialize - // the environment. - // - // This functionality will be removed once rust 1.69.0 is stable, at which - // point wasi-libc will no longer have this behavior. - // - // See - // https://github.com/bytecodealliance/preview2-prototyping/issues/99 - // for more details. - #[cfg(target_arch="wasm32")] - ::cargo_component_bindings::rt::run_ctors_once(); - - use super::super::super::super::miden::base::types::Asset as V0; - let v0 = match arg0 { - 0 => { - let e0 = super::super::super::super::miden::base::types::FungibleAsset{ - asset: arg1 as u64, - amount: arg2 as u64, - }; - V0::Fungible(e0) - } - n => { - debug_assert_eq!(n, 1, "invalid enum discriminant"); - let e0 = (arg1 as u64, arg2 as u64, arg3 as u64, arg4 as u64); - V0::NonFungible(e0) - } - }; - <_GuestImpl as Guest>::send_asset(v0, arg5 as u64, (arg6 as u64, arg7 as u64, arg8 as u64, arg9 as u64)); - } - }; - use super::super::super::super::super::Component as _GuestImpl; - pub trait Guest { - fn receive_asset(asset: Asset,); - fn send_asset(asset: Asset,tag: Tag,recipient: Recipient,); - } - - } - - } - } -} - -#[cfg(target_arch = "wasm32")] -#[link_section = "component-type:basic-wallet-world"] -#[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1410] = [3, 0, 18, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 45, 119, 111, 114, 108, 100, 0, 97, 115, 109, 13, 0, 1, 0, 7, 170, 3, 1, 65, 7, 1, 66, 14, 1, 119, 4, 0, 4, 102, 101, 108, 116, 3, 0, 0, 1, 111, 4, 1, 1, 1, 1, 4, 0, 4, 119, 111, 114, 100, 3, 0, 2, 4, 0, 10, 97, 99, 99, 111, 117, 110, 116, 45, 105, 100, 3, 0, 1, 4, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 3, 0, 3, 4, 0, 3, 116, 97, 103, 3, 0, 1, 1, 114, 2, 5, 97, 115, 115, 101, 116, 4, 6, 97, 109, 111, 117, 110, 116, 119, 4, 0, 14, 102, 117, 110, 103, 105, 98, 108, 101, 45, 97, 115, 115, 101, 116, 3, 0, 7, 4, 0, 18, 110, 111, 110, 45, 102, 117, 110, 103, 105, 98, 108, 101, 45, 97, 115, 115, 101, 116, 3, 0, 3, 1, 113, 2, 8, 102, 117, 110, 103, 105, 98, 108, 101, 1, 8, 0, 12, 110, 111, 110, 45, 102, 117, 110, 103, 105, 98, 108, 101, 1, 9, 0, 4, 0, 5, 97, 115, 115, 101, 116, 3, 0, 10, 1, 112, 1, 4, 0, 11, 110, 111, 116, 101, 45, 105, 110, 112, 117, 116, 115, 3, 0, 12, 3, 1, 22, 109, 105, 100, 101, 110, 58, 98, 97, 115, 101, 47, 116, 121, 112, 101, 115, 64, 49, 46, 48, 46, 48, 5, 0, 2, 3, 0, 0, 5, 97, 115, 115, 101, 116, 2, 3, 0, 0, 3, 116, 97, 103, 2, 3, 0, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 1, 66, 10, 2, 3, 2, 1, 1, 4, 0, 5, 97, 115, 115, 101, 116, 3, 0, 0, 2, 3, 2, 1, 2, 4, 0, 3, 116, 97, 103, 3, 0, 2, 2, 3, 2, 1, 3, 4, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 3, 0, 4, 1, 64, 1, 5, 97, 115, 115, 101, 116, 1, 1, 0, 4, 0, 13, 114, 101, 99, 101, 105, 118, 101, 45, 97, 115, 115, 101, 116, 1, 6, 1, 64, 3, 5, 97, 115, 115, 101, 116, 1, 3, 116, 97, 103, 3, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 5, 1, 0, 4, 0, 10, 115, 101, 110, 100, 45, 97, 115, 115, 101, 116, 1, 7, 4, 1, 37, 109, 105, 100, 101, 110, 58, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 47, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 64, 49, 46, 48, 46, 48, 5, 4, 11, 18, 1, 0, 12, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 3, 0, 0, 7, 173, 6, 1, 65, 2, 1, 65, 13, 1, 66, 14, 1, 119, 4, 0, 4, 102, 101, 108, 116, 3, 0, 0, 1, 111, 4, 1, 1, 1, 1, 4, 0, 4, 119, 111, 114, 100, 3, 0, 2, 4, 0, 10, 97, 99, 99, 111, 117, 110, 116, 45, 105, 100, 3, 0, 1, 4, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 3, 0, 3, 4, 0, 3, 116, 97, 103, 3, 0, 1, 1, 114, 2, 5, 97, 115, 115, 101, 116, 4, 6, 97, 109, 111, 117, 110, 116, 119, 4, 0, 14, 102, 117, 110, 103, 105, 98, 108, 101, 45, 97, 115, 115, 101, 116, 3, 0, 7, 4, 0, 18, 110, 111, 110, 45, 102, 117, 110, 103, 105, 98, 108, 101, 45, 97, 115, 115, 101, 116, 3, 0, 3, 1, 113, 2, 8, 102, 117, 110, 103, 105, 98, 108, 101, 1, 8, 0, 12, 110, 111, 110, 45, 102, 117, 110, 103, 105, 98, 108, 101, 1, 9, 0, 4, 0, 5, 97, 115, 115, 101, 116, 3, 0, 10, 1, 112, 1, 4, 0, 11, 110, 111, 116, 101, 45, 105, 110, 112, 117, 116, 115, 3, 0, 12, 3, 1, 22, 109, 105, 100, 101, 110, 58, 98, 97, 115, 101, 47, 116, 121, 112, 101, 115, 64, 49, 46, 48, 46, 48, 5, 0, 2, 3, 0, 0, 5, 97, 115, 115, 101, 116, 2, 3, 0, 0, 3, 116, 97, 103, 2, 3, 0, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 2, 3, 0, 0, 11, 110, 111, 116, 101, 45, 105, 110, 112, 117, 116, 115, 2, 3, 0, 0, 10, 97, 99, 99, 111, 117, 110, 116, 45, 105, 100, 1, 66, 22, 2, 3, 2, 1, 1, 4, 0, 5, 97, 115, 115, 101, 116, 3, 0, 0, 2, 3, 2, 1, 2, 4, 0, 3, 116, 97, 103, 3, 0, 2, 2, 3, 2, 1, 3, 4, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 3, 0, 4, 2, 3, 2, 1, 4, 4, 0, 11, 110, 111, 116, 101, 45, 105, 110, 112, 117, 116, 115, 3, 0, 6, 2, 3, 2, 1, 5, 4, 0, 10, 97, 99, 99, 111, 117, 110, 116, 45, 105, 100, 3, 0, 8, 1, 64, 0, 0, 9, 4, 0, 6, 103, 101, 116, 45, 105, 100, 1, 10, 1, 64, 1, 5, 97, 115, 115, 101, 116, 1, 0, 1, 4, 0, 9, 97, 100, 100, 45, 97, 115, 115, 101, 116, 1, 11, 4, 0, 12, 114, 101, 109, 111, 118, 101, 45, 97, 115, 115, 101, 116, 1, 11, 1, 64, 3, 5, 97, 115, 115, 101, 116, 1, 3, 116, 97, 103, 3, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 5, 1, 0, 4, 0, 11, 99, 114, 101, 97, 116, 101, 45, 110, 111, 116, 101, 1, 12, 1, 64, 0, 0, 7, 4, 0, 10, 103, 101, 116, 45, 105, 110, 112, 117, 116, 115, 1, 13, 1, 112, 1, 1, 64, 0, 0, 14, 4, 0, 10, 103, 101, 116, 45, 97, 115, 115, 101, 116, 115, 1, 15, 3, 1, 26, 109, 105, 100, 101, 110, 58, 98, 97, 115, 101, 47, 116, 120, 45, 107, 101, 114, 110, 101, 108, 64, 49, 46, 48, 46, 48, 5, 6, 1, 66, 0, 4, 1, 24, 109, 105, 100, 101, 110, 58, 98, 97, 115, 101, 47, 97, 99, 99, 111, 117, 110, 116, 64, 49, 46, 48, 46, 48, 5, 7, 1, 66, 10, 2, 3, 2, 1, 1, 4, 0, 5, 97, 115, 115, 101, 116, 3, 0, 0, 2, 3, 2, 1, 2, 4, 0, 3, 116, 97, 103, 3, 0, 2, 2, 3, 2, 1, 3, 4, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 3, 0, 4, 1, 64, 1, 5, 97, 115, 115, 101, 116, 1, 1, 0, 4, 0, 13, 114, 101, 99, 101, 105, 118, 101, 45, 97, 115, 115, 101, 116, 1, 6, 1, 64, 3, 5, 97, 115, 115, 101, 116, 1, 3, 116, 97, 103, 3, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 5, 1, 0, 4, 0, 10, 115, 101, 110, 100, 45, 97, 115, 115, 101, 116, 1, 7, 4, 1, 37, 109, 105, 100, 101, 110, 58, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 47, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 64, 49, 46, 48, 46, 48, 5, 8, 4, 1, 43, 109, 105, 100, 101, 110, 58, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 47, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 45, 119, 111, 114, 108, 100, 64, 49, 46, 48, 46, 48, 4, 0, 11, 24, 1, 0, 18, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 45, 119, 111, 114, 108, 100, 3, 2, 0, 0, 16, 12, 112, 97, 99, 107, 97, 103, 101, 45, 100, 111, 99, 115, 0, 123, 125, 0, 70, 9, 112, 114, 111, 100, 117, 99, 101, 114, 115, 1, 12, 112, 114, 111, 99, 101, 115, 115, 101, 100, 45, 98, 121, 2, 13, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 6, 48, 46, 49, 56, 46, 50, 16, 119, 105, 116, 45, 98, 105, 110, 100, 103, 101, 110, 45, 114, 117, 115, 116, 6, 48, 46, 49, 54, 46, 48]; - -#[inline(never)] -#[doc(hidden)] -#[cfg(target_arch = "wasm32")] -pub fn __link_section() {} diff --git a/tests/integration/expected/sdk_basic_wallet/bindings/basic_wallet_p2id_note_bindings.rs b/tests/integration/expected/sdk_basic_wallet/bindings/basic_wallet_p2id_note_bindings.rs deleted file mode 100644 index 35e0ffe86..000000000 --- a/tests/integration/expected/sdk_basic_wallet/bindings/basic_wallet_p2id_note_bindings.rs +++ /dev/null @@ -1,557 +0,0 @@ -// Generated by `wit-bindgen` 0.16.0. DO NOT EDIT! -pub mod miden { - pub mod base { - - #[allow(clippy::all)] - pub mod types { - #[used] - #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = super::super::super::__link_section; - /// Represents base field element in the field using Montgomery representation. - /// Internal values represent x * R mod M where R = 2^64 mod M and x in [0, M). - /// The backing type is `u64` but the internal values are always in the range [0, M). - /// Field modulus M = 2^64 - 2^32 + 1 - pub type Felt = u64; - /// A group of four field elements in the Miden base field. - pub type Word = (Felt,Felt,Felt,Felt,); - /// Unique identifier of an account. - /// - /// Account ID consists of 1 field element (~64 bits). This field element uniquely identifies a - /// single account and also specifies the type of the underlying account. Specifically: - /// - The two most significant bits of the ID specify the type of the account: - /// - 00 - regular account with updatable code. - /// - 01 - regular account with immutable code. - /// - 10 - fungible asset faucet with immutable code. - /// - 11 - non-fungible asset faucet with immutable code. - /// - The third most significant bit of the ID specifies whether the account data is stored on-chain: - /// - 0 - full account data is stored on-chain. - /// - 1 - only the account hash is stored on-chain which serves as a commitment to the account state. - /// As such the three most significant bits fully describes the type of the account. - pub type AccountId = Felt; - /// Recipient of the note, i.e., hash(hash(hash(serial_num, [0; 4]), note_script_hash), input_hash) - pub type Recipient = Word; - pub type Tag = Felt; - /// A fungible asset - #[repr(C)] - #[derive(Clone, Copy)] - pub struct FungibleAsset { - /// Faucet ID of the faucet which issued the asset as well as the asset amount. - pub asset: AccountId, - /// Asset amount is guaranteed to be 2^63 - 1 or smaller. - pub amount: u64, - } - impl ::core::fmt::Debug for FungibleAsset { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - f.debug_struct("FungibleAsset").field("asset", &self.asset).field("amount", &self.amount).finish() - } - } - /// A commitment to a non-fungible asset. - /// - /// A non-fungible asset consists of 4 field elements which are computed by hashing asset data - /// (which can be of arbitrary length) to produce: [d0, d1, d2, d3]. We then replace d1 with the - /// faucet_id that issued the asset: [d0, faucet_id, d2, d3]. We then set the most significant bit - /// of the most significant element to ZERO. - pub type NonFungibleAsset = Word; - /// A fungible or a non-fungible asset. - /// - /// All assets are encoded using a single word (4 elements) such that it is easy to determine the - /// type of an asset both inside and outside Miden VM. Specifically: - /// Element 1 will be: - /// - ZERO for a fungible asset - /// - non-ZERO for a non-fungible asset - /// The most significant bit will be: - /// - ONE for a fungible asset - /// - ZERO for a non-fungible asset - /// - /// The above properties guarantee that there can never be a collision between a fungible and a - /// non-fungible asset. - /// - /// The methodology for constructing fungible and non-fungible assets is described below. - /// - /// # Fungible assets - /// The most significant element of a fungible asset is set to the ID of the faucet which issued - /// the asset. This guarantees the properties described above (the first bit is ONE). - /// - /// The least significant element is set to the amount of the asset. This amount cannot be greater - /// than 2^63 - 1 and thus requires 63-bits to store. - /// - /// Elements 1 and 2 are set to ZERO. - /// - /// It is impossible to find a collision between two fungible assets issued by different faucets as - /// the faucet_id is included in the description of the asset and this is guaranteed to be different - /// for each faucet as per the faucet creation logic. - /// - /// # Non-fungible assets - /// The 4 elements of non-fungible assets are computed as follows: - /// - First the asset data is hashed. This compresses an asset of an arbitrary length to 4 field - /// elements: [d0, d1, d2, d3]. - /// - d1 is then replaced with the faucet_id which issues the asset: [d0, faucet_id, d2, d3]. - /// - Lastly, the most significant bit of d3 is set to ZERO. - /// - /// It is impossible to find a collision between two non-fungible assets issued by different faucets - /// as the faucet_id is included in the description of the non-fungible asset and this is guaranteed - /// to be different as per the faucet creation logic. Collision resistance for non-fungible assets - /// issued by the same faucet is ~2^95. - #[derive(Clone, Copy)] - pub enum Asset{ - Fungible(FungibleAsset), - NonFungible(NonFungibleAsset), - } - impl ::core::fmt::Debug for Asset { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - match self { - Asset::Fungible(e) => { - f.debug_tuple("Asset::Fungible").field(e).finish() - } - Asset::NonFungible(e) => { - f.debug_tuple("Asset::NonFungible").field(e).finish() - } - } - } - } - /// Inputs of the currently executed note, never exceeds 16 felts - pub type NoteInputs = ::cargo_component_bindings::rt::vec::Vec::; - - } - - - #[allow(clippy::all)] - pub mod tx_kernel { - #[used] - #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = super::super::super::__link_section; - pub type Asset = super::super::super::miden::base::types::Asset; - pub type Tag = super::super::super::miden::base::types::Tag; - pub type Recipient = super::super::super::miden::base::types::Recipient; - pub type NoteInputs = super::super::super::miden::base::types::NoteInputs; - pub type AccountId = super::super::super::miden::base::types::AccountId; - #[allow(unused_unsafe, clippy::all)] - /// Account-related functions - /// Get the id of the currently executing account - pub fn get_id() -> AccountId{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "get-id"] - fn wit_import() -> i64; - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import() -> i64{ unreachable!() } - let ret = wit_import(); - ret as u64 - } - } - #[allow(unused_unsafe, clippy::all)] - /// Add the specified asset to the vault - pub fn add_asset(asset: Asset,) -> Asset{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[repr(align(8))] - struct RetArea([u8; 40]); - let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - use super::super::super::miden::base::types::Asset as V2; - let (result3_0,result3_1,result3_2,result3_3,result3_4,) = match asset { - V2::Fungible(e) => { - let super::super::super::miden::base::types::FungibleAsset{ asset:asset0, amount:amount0, } = e; - - (0i32, ::cargo_component_bindings::rt::as_i64(asset0), ::cargo_component_bindings::rt::as_i64(amount0), 0i64, 0i64) - }, - V2::NonFungible(e) => { - let (t1_0, t1_1, t1_2, t1_3, ) = e; - - (1i32, ::cargo_component_bindings::rt::as_i64(t1_0), ::cargo_component_bindings::rt::as_i64(t1_1), ::cargo_component_bindings::rt::as_i64(t1_2), ::cargo_component_bindings::rt::as_i64(t1_3)) - }, - }; - let ptr4 = ret_area.as_mut_ptr() as i32; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "add-asset"] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i32, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i32, ){ unreachable!() } - wit_import(result3_0, result3_1, result3_2, result3_3, result3_4, ptr4); - let l5 = i32::from(*((ptr4 + 0) as *const u8)); - use super::super::super::miden::base::types::Asset as V12; - let v12 = match l5 { - 0 => { - let e12 = { - let l6 = *((ptr4 + 8) as *const i64); - let l7 = *((ptr4 + 16) as *const i64); - - super::super::super::miden::base::types::FungibleAsset{ - asset: l6 as u64, - amount: l7 as u64, - } - }; - V12::Fungible(e12) - } - n => { - debug_assert_eq!(n, 1, "invalid enum discriminant"); - let e12 = { - let l8 = *((ptr4 + 8) as *const i64); - let l9 = *((ptr4 + 16) as *const i64); - let l10 = *((ptr4 + 24) as *const i64); - let l11 = *((ptr4 + 32) as *const i64); - - (l8 as u64, l9 as u64, l10 as u64, l11 as u64) - }; - V12::NonFungible(e12) - } - }; - v12 - } - } - #[allow(unused_unsafe, clippy::all)] - /// Remove the specified asset from the vault - pub fn remove_asset(asset: Asset,) -> Asset{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[repr(align(8))] - struct RetArea([u8; 40]); - let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - use super::super::super::miden::base::types::Asset as V2; - let (result3_0,result3_1,result3_2,result3_3,result3_4,) = match asset { - V2::Fungible(e) => { - let super::super::super::miden::base::types::FungibleAsset{ asset:asset0, amount:amount0, } = e; - - (0i32, ::cargo_component_bindings::rt::as_i64(asset0), ::cargo_component_bindings::rt::as_i64(amount0), 0i64, 0i64) - }, - V2::NonFungible(e) => { - let (t1_0, t1_1, t1_2, t1_3, ) = e; - - (1i32, ::cargo_component_bindings::rt::as_i64(t1_0), ::cargo_component_bindings::rt::as_i64(t1_1), ::cargo_component_bindings::rt::as_i64(t1_2), ::cargo_component_bindings::rt::as_i64(t1_3)) - }, - }; - let ptr4 = ret_area.as_mut_ptr() as i32; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "remove-asset"] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i32, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i32, ){ unreachable!() } - wit_import(result3_0, result3_1, result3_2, result3_3, result3_4, ptr4); - let l5 = i32::from(*((ptr4 + 0) as *const u8)); - use super::super::super::miden::base::types::Asset as V12; - let v12 = match l5 { - 0 => { - let e12 = { - let l6 = *((ptr4 + 8) as *const i64); - let l7 = *((ptr4 + 16) as *const i64); - - super::super::super::miden::base::types::FungibleAsset{ - asset: l6 as u64, - amount: l7 as u64, - } - }; - V12::Fungible(e12) - } - n => { - debug_assert_eq!(n, 1, "invalid enum discriminant"); - let e12 = { - let l8 = *((ptr4 + 8) as *const i64); - let l9 = *((ptr4 + 16) as *const i64); - let l10 = *((ptr4 + 24) as *const i64); - let l11 = *((ptr4 + 32) as *const i64); - - (l8 as u64, l9 as u64, l10 as u64, l11 as u64) - }; - V12::NonFungible(e12) - } - }; - v12 - } - } - #[allow(unused_unsafe, clippy::all)] - /// Note-related functions - /// Creates a new note. - /// asset is the asset to be included in the note. - /// tag is the tag to be included in the note. - /// recipient is the recipient of the note. - pub fn create_note(asset: Asset,tag: Tag,recipient: Recipient,){ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - use super::super::super::miden::base::types::Asset as V2; - let (result3_0,result3_1,result3_2,result3_3,result3_4,) = match asset { - V2::Fungible(e) => { - let super::super::super::miden::base::types::FungibleAsset{ asset:asset0, amount:amount0, } = e; - - (0i32, ::cargo_component_bindings::rt::as_i64(asset0), ::cargo_component_bindings::rt::as_i64(amount0), 0i64, 0i64) - }, - V2::NonFungible(e) => { - let (t1_0, t1_1, t1_2, t1_3, ) = e; - - (1i32, ::cargo_component_bindings::rt::as_i64(t1_0), ::cargo_component_bindings::rt::as_i64(t1_1), ::cargo_component_bindings::rt::as_i64(t1_2), ::cargo_component_bindings::rt::as_i64(t1_3)) - }, - }; - let (t4_0, t4_1, t4_2, t4_3, ) = recipient; - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "create-note"] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, ){ unreachable!() } - wit_import(result3_0, result3_1, result3_2, result3_3, result3_4, ::cargo_component_bindings::rt::as_i64(tag), ::cargo_component_bindings::rt::as_i64(t4_0), ::cargo_component_bindings::rt::as_i64(t4_1), ::cargo_component_bindings::rt::as_i64(t4_2), ::cargo_component_bindings::rt::as_i64(t4_3)); - } - } - #[allow(unused_unsafe, clippy::all)] - /// Get the inputs of the currently executed note - pub fn get_inputs() -> NoteInputs{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[repr(align(4))] - struct RetArea([u8; 8]); - let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - let ptr0 = ret_area.as_mut_ptr() as i32; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "get-inputs"] - fn wit_import(_: i32, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, ){ unreachable!() } - wit_import(ptr0); - let l1 = *((ptr0 + 0) as *const i32); - let l2 = *((ptr0 + 4) as *const i32); - let len3 = l2 as usize; - Vec::from_raw_parts(l1 as *mut _, len3, len3) - } - } - #[allow(unused_unsafe, clippy::all)] - /// Get the assets of the currently executing note - pub fn get_assets() -> ::cargo_component_bindings::rt::vec::Vec::{ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - - #[repr(align(4))] - struct RetArea([u8; 8]); - let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - let ptr0 = ret_area.as_mut_ptr() as i32; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:base/tx-kernel@1.0.0")] - extern "C" { - #[link_name = "get-assets"] - fn wit_import(_: i32, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, ){ unreachable!() } - wit_import(ptr0); - let l1 = *((ptr0 + 0) as *const i32); - let l2 = *((ptr0 + 4) as *const i32); - let base11 = l1; - let len11 = l2; - let mut result11 = Vec::with_capacity(len11 as usize); - for i in 0..len11 { - let base = base11 + i * 40; - let e11 = { - let l3 = i32::from(*((base + 0) as *const u8)); - use super::super::super::miden::base::types::Asset as V10; - let v10 = match l3 { - 0 => { - let e10 = { - let l4 = *((base + 8) as *const i64); - let l5 = *((base + 16) as *const i64); - - super::super::super::miden::base::types::FungibleAsset{ - asset: l4 as u64, - amount: l5 as u64, - } - }; - V10::Fungible(e10) - } - n => { - debug_assert_eq!(n, 1, "invalid enum discriminant"); - let e10 = { - let l6 = *((base + 8) as *const i64); - let l7 = *((base + 16) as *const i64); - let l8 = *((base + 24) as *const i64); - let l9 = *((base + 32) as *const i64); - - (l6 as u64, l7 as u64, l8 as u64, l9 as u64) - }; - V10::NonFungible(e10) - } - }; - - v10 - }; - result11.push(e11); - } - ::cargo_component_bindings::rt::dealloc(base11, (len11 as usize) * 40, 8); - result11 - } - } - - } - - } - pub mod basic_wallet { - - #[allow(clippy::all)] - pub mod basic_wallet { - #[used] - #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = super::super::super::__link_section; - pub type Asset = super::super::super::miden::base::types::Asset; - pub type Tag = super::super::super::miden::base::types::Tag; - pub type Recipient = super::super::super::miden::base::types::Recipient; - #[allow(unused_unsafe, clippy::all)] - pub fn receive_asset(asset: Asset,){ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - use super::super::super::miden::base::types::Asset as V2; - let (result3_0,result3_1,result3_2,result3_3,result3_4,) = match asset { - V2::Fungible(e) => { - let super::super::super::miden::base::types::FungibleAsset{ asset:asset0, amount:amount0, } = e; - - (0i32, ::cargo_component_bindings::rt::as_i64(asset0), ::cargo_component_bindings::rt::as_i64(amount0), 0i64, 0i64) - }, - V2::NonFungible(e) => { - let (t1_0, t1_1, t1_2, t1_3, ) = e; - - (1i32, ::cargo_component_bindings::rt::as_i64(t1_0), ::cargo_component_bindings::rt::as_i64(t1_1), ::cargo_component_bindings::rt::as_i64(t1_2), ::cargo_component_bindings::rt::as_i64(t1_3)) - }, - }; - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:basic-wallet/basic-wallet@1.0.0")] - extern "C" { - #[link_name = "receive-asset"] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, ){ unreachable!() } - wit_import(result3_0, result3_1, result3_2, result3_3, result3_4); - } - } - #[allow(unused_unsafe, clippy::all)] - pub fn send_asset(asset: Asset,tag: Tag,recipient: Recipient,){ - - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - unsafe { - use super::super::super::miden::base::types::Asset as V2; - let (result3_0,result3_1,result3_2,result3_3,result3_4,) = match asset { - V2::Fungible(e) => { - let super::super::super::miden::base::types::FungibleAsset{ asset:asset0, amount:amount0, } = e; - - (0i32, ::cargo_component_bindings::rt::as_i64(asset0), ::cargo_component_bindings::rt::as_i64(amount0), 0i64, 0i64) - }, - V2::NonFungible(e) => { - let (t1_0, t1_1, t1_2, t1_3, ) = e; - - (1i32, ::cargo_component_bindings::rt::as_i64(t1_0), ::cargo_component_bindings::rt::as_i64(t1_1), ::cargo_component_bindings::rt::as_i64(t1_2), ::cargo_component_bindings::rt::as_i64(t1_3)) - }, - }; - let (t4_0, t4_1, t4_2, t4_3, ) = recipient; - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "miden:basic-wallet/basic-wallet@1.0.0")] - extern "C" { - #[link_name = "send-asset"] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, ); - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import(_: i32, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, _: i64, ){ unreachable!() } - wit_import(result3_0, result3_1, result3_2, result3_3, result3_4, ::cargo_component_bindings::rt::as_i64(tag), ::cargo_component_bindings::rt::as_i64(t4_0), ::cargo_component_bindings::rt::as_i64(t4_1), ::cargo_component_bindings::rt::as_i64(t4_2), ::cargo_component_bindings::rt::as_i64(t4_3)); - } - } - - } - - } -} -pub mod exports { - pub mod miden { - pub mod base { - - #[allow(clippy::all)] - pub mod note { - #[used] - #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_section; - const _: () = { - - #[doc(hidden)] - #[export_name = "miden:base/note@1.0.0#note-script"] - #[allow(non_snake_case)] - unsafe extern "C" fn __export_note_script() { - #[allow(unused_imports)] - use ::cargo_component_bindings::rt::{alloc, vec::Vec, string::String}; - - // Before executing any other code, use this function to run all static - // constructors, if they have not yet been run. This is a hack required - // to work around wasi-libc ctors calling import functions to initialize - // the environment. - // - // This functionality will be removed once rust 1.69.0 is stable, at which - // point wasi-libc will no longer have this behavior. - // - // See - // https://github.com/bytecodealliance/preview2-prototyping/issues/99 - // for more details. - #[cfg(target_arch="wasm32")] - ::cargo_component_bindings::rt::run_ctors_once(); - - <_GuestImpl as Guest>::note_script(); - } - }; - use super::super::super::super::super::Component as _GuestImpl; - pub trait Guest { - fn note_script(); - } - - } - - } - } -} - -#[cfg(target_arch = "wasm32")] -#[link_section = "component-type:notes-world"] -#[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 950] = [3, 0, 11, 110, 111, 116, 101, 115, 45, 119, 111, 114, 108, 100, 0, 97, 115, 109, 13, 0, 1, 0, 7, 176, 6, 1, 65, 2, 1, 65, 13, 1, 66, 14, 1, 119, 4, 0, 4, 102, 101, 108, 116, 3, 0, 0, 1, 111, 4, 1, 1, 1, 1, 4, 0, 4, 119, 111, 114, 100, 3, 0, 2, 4, 0, 10, 97, 99, 99, 111, 117, 110, 116, 45, 105, 100, 3, 0, 1, 4, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 3, 0, 3, 4, 0, 3, 116, 97, 103, 3, 0, 1, 1, 114, 2, 5, 97, 115, 115, 101, 116, 4, 6, 97, 109, 111, 117, 110, 116, 119, 4, 0, 14, 102, 117, 110, 103, 105, 98, 108, 101, 45, 97, 115, 115, 101, 116, 3, 0, 7, 4, 0, 18, 110, 111, 110, 45, 102, 117, 110, 103, 105, 98, 108, 101, 45, 97, 115, 115, 101, 116, 3, 0, 3, 1, 113, 2, 8, 102, 117, 110, 103, 105, 98, 108, 101, 1, 8, 0, 12, 110, 111, 110, 45, 102, 117, 110, 103, 105, 98, 108, 101, 1, 9, 0, 4, 0, 5, 97, 115, 115, 101, 116, 3, 0, 10, 1, 112, 1, 4, 0, 11, 110, 111, 116, 101, 45, 105, 110, 112, 117, 116, 115, 3, 0, 12, 3, 1, 22, 109, 105, 100, 101, 110, 58, 98, 97, 115, 101, 47, 116, 121, 112, 101, 115, 64, 49, 46, 48, 46, 48, 5, 0, 2, 3, 0, 0, 5, 97, 115, 115, 101, 116, 2, 3, 0, 0, 3, 116, 97, 103, 2, 3, 0, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 2, 3, 0, 0, 11, 110, 111, 116, 101, 45, 105, 110, 112, 117, 116, 115, 2, 3, 0, 0, 10, 97, 99, 99, 111, 117, 110, 116, 45, 105, 100, 1, 66, 22, 2, 3, 2, 1, 1, 4, 0, 5, 97, 115, 115, 101, 116, 3, 0, 0, 2, 3, 2, 1, 2, 4, 0, 3, 116, 97, 103, 3, 0, 2, 2, 3, 2, 1, 3, 4, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 3, 0, 4, 2, 3, 2, 1, 4, 4, 0, 11, 110, 111, 116, 101, 45, 105, 110, 112, 117, 116, 115, 3, 0, 6, 2, 3, 2, 1, 5, 4, 0, 10, 97, 99, 99, 111, 117, 110, 116, 45, 105, 100, 3, 0, 8, 1, 64, 0, 0, 9, 4, 0, 6, 103, 101, 116, 45, 105, 100, 1, 10, 1, 64, 1, 5, 97, 115, 115, 101, 116, 1, 0, 1, 4, 0, 9, 97, 100, 100, 45, 97, 115, 115, 101, 116, 1, 11, 4, 0, 12, 114, 101, 109, 111, 118, 101, 45, 97, 115, 115, 101, 116, 1, 11, 1, 64, 3, 5, 97, 115, 115, 101, 116, 1, 3, 116, 97, 103, 3, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 5, 1, 0, 4, 0, 11, 99, 114, 101, 97, 116, 101, 45, 110, 111, 116, 101, 1, 12, 1, 64, 0, 0, 7, 4, 0, 10, 103, 101, 116, 45, 105, 110, 112, 117, 116, 115, 1, 13, 1, 112, 1, 1, 64, 0, 0, 14, 4, 0, 10, 103, 101, 116, 45, 97, 115, 115, 101, 116, 115, 1, 15, 3, 1, 26, 109, 105, 100, 101, 110, 58, 98, 97, 115, 101, 47, 116, 120, 45, 107, 101, 114, 110, 101, 108, 64, 49, 46, 48, 46, 48, 5, 6, 1, 66, 10, 2, 3, 2, 1, 1, 4, 0, 5, 97, 115, 115, 101, 116, 3, 0, 0, 2, 3, 2, 1, 2, 4, 0, 3, 116, 97, 103, 3, 0, 2, 2, 3, 2, 1, 3, 4, 0, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 3, 0, 4, 1, 64, 1, 5, 97, 115, 115, 101, 116, 1, 1, 0, 4, 0, 13, 114, 101, 99, 101, 105, 118, 101, 45, 97, 115, 115, 101, 116, 1, 6, 1, 64, 3, 5, 97, 115, 115, 101, 116, 1, 3, 116, 97, 103, 3, 9, 114, 101, 99, 105, 112, 105, 101, 110, 116, 5, 1, 0, 4, 0, 10, 115, 101, 110, 100, 45, 97, 115, 115, 101, 116, 1, 7, 3, 1, 37, 109, 105, 100, 101, 110, 58, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 47, 98, 97, 115, 105, 99, 45, 119, 97, 108, 108, 101, 116, 64, 49, 46, 48, 46, 48, 5, 7, 1, 66, 2, 1, 64, 0, 1, 0, 4, 0, 11, 110, 111, 116, 101, 45, 115, 99, 114, 105, 112, 116, 1, 0, 4, 1, 21, 109, 105, 100, 101, 110, 58, 98, 97, 115, 101, 47, 110, 111, 116, 101, 64, 49, 46, 48, 46, 48, 5, 8, 4, 1, 28, 109, 105, 100, 101, 110, 58, 112, 50, 105, 100, 47, 110, 111, 116, 101, 115, 45, 119, 111, 114, 108, 100, 64, 49, 46, 48, 46, 48, 4, 0, 11, 17, 1, 0, 11, 110, 111, 116, 101, 115, 45, 119, 111, 114, 108, 100, 3, 0, 0, 0, 16, 12, 112, 97, 99, 107, 97, 103, 101, 45, 100, 111, 99, 115, 0, 123, 125, 0, 70, 9, 112, 114, 111, 100, 117, 99, 101, 114, 115, 1, 12, 112, 114, 111, 99, 101, 115, 115, 101, 100, 45, 98, 121, 2, 13, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 6, 48, 46, 49, 56, 46, 50, 16, 119, 105, 116, 45, 98, 105, 110, 100, 103, 101, 110, 45, 114, 117, 115, 116, 6, 48, 46, 49, 54, 46, 48]; - -#[inline(never)] -#[doc(hidden)] -#[cfg(target_arch = "wasm32")] -pub fn __link_section() {} diff --git a/tests/integration/expected/sdk_basic_wallet/miden_sdk.wat b/tests/integration/expected/sdk_basic_wallet/miden_sdk.wat deleted file mode 100644 index 77948aa90..000000000 --- a/tests/integration/expected/sdk_basic_wallet/miden_sdk.wat +++ /dev/null @@ -1,6116 +0,0 @@ -(component - (core module (;0;) - (type (;0;) (func (param i32 i32) (result i32))) - (type (;1;) (func (param i32 i32 i32 i32) (result i32))) - (type (;2;) (func (param i32) (result i32))) - (type (;3;) (func (param i32))) - (type (;4;) (func (param i32 i32))) - (type (;5;) (func)) - (type (;6;) (func (param i32 i32 i32) (result i32))) - (func $__rust_alloc (;0;) (type 0) (param i32 i32) (result i32) - (local i32) - local.get 0 - local.get 1 - call $__rdl_alloc - local.set 2 - local.get 2 - return - ) - (func $__rust_realloc (;1;) (type 1) (param i32 i32 i32 i32) (result i32) - (local i32) - local.get 0 - local.get 1 - local.get 2 - local.get 3 - call $__rdl_realloc - local.set 4 - local.get 4 - return - ) - (func $__rdl_alloc (;2;) (type 0) (param i32 i32) (result i32) - (local i32) - block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.const 8 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - local.get 0 - i32.le_u - br_if 1 (;@1;) - end - local.get 1 - local.get 1 - local.get 0 - local.get 1 - i32.rem_u - local.tee 2 - i32.sub - i32.const 0 - local.get 2 - select - local.get 0 - i32.add - call $aligned_alloc - return - end - local.get 0 - call $malloc - ) - (func $__rdl_realloc (;3;) (type 1) (param i32 i32 i32 i32) (result i32) - (local i32 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 8 - i32.gt_u - br_if 0 (;@3;) - local.get 2 - local.get 3 - i32.le_u - br_if 1 (;@2;) - end - i32.const 0 - local.set 4 - local.get 2 - local.get 2 - local.get 3 - local.get 2 - i32.rem_u - local.tee 5 - i32.sub - i32.const 0 - local.get 5 - select - local.get 3 - i32.add - call $aligned_alloc - local.tee 2 - i32.eqz - br_if 1 (;@1;) - local.get 2 - local.get 0 - local.get 1 - local.get 3 - local.get 1 - local.get 3 - i32.lt_u - select - call $memcpy - local.set 2 - local.get 0 - call $free - local.get 2 - return - end - local.get 0 - local.get 3 - call $realloc - local.set 4 - end - local.get 4 - ) - (func $malloc (;4;) (type 2) (param i32) (result i32) - local.get 0 - call $dlmalloc - ) - (func $dlmalloc (;5;) (type 2) (param i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - global.get $__stack_pointer - i32.const 16 - i32.sub - local.tee 1 - global.set $__stack_pointer - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - block ;; label = @12 - i32.const 0 - i32.load offset=1048604 - local.tee 2 - br_if 0 (;@12;) - block ;; label = @13 - i32.const 0 - i32.load offset=1049052 - local.tee 3 - br_if 0 (;@13;) - i32.const 0 - i64.const -1 - i64.store offset=1049064 align=4 - i32.const 0 - i64.const 281474976776192 - i64.store offset=1049056 align=4 - i32.const 0 - local.get 1 - i32.const 8 - i32.add - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - local.tee 3 - i32.store offset=1049052 - i32.const 0 - i32.const 0 - i32.store offset=1049072 - i32.const 0 - i32.const 0 - i32.store offset=1049024 - end - i32.const 1114112 - i32.const 1049088 - i32.lt_u - br_if 1 (;@11;) - i32.const 0 - local.set 2 - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.const 89 - i32.lt_u - br_if 0 (;@12;) - i32.const 0 - local.set 4 - i32.const 0 - i32.const 1049088 - i32.store offset=1049028 - i32.const 0 - i32.const 1049088 - i32.store offset=1048596 - i32.const 0 - local.get 3 - i32.store offset=1048616 - i32.const 0 - i32.const -1 - i32.store offset=1048612 - i32.const 0 - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.store offset=1049032 - loop ;; label = @13 - local.get 4 - i32.const 1048640 - i32.add - local.get 4 - i32.const 1048628 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 4 - i32.const 1048620 - i32.add - local.tee 5 - i32.store - local.get 4 - i32.const 1048632 - i32.add - local.get 5 - i32.store - local.get 4 - i32.const 1048648 - i32.add - local.get 4 - i32.const 1048636 - i32.add - local.tee 5 - i32.store - local.get 5 - local.get 3 - i32.store - local.get 4 - i32.const 1048656 - i32.add - local.get 4 - i32.const 1048644 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 5 - i32.store - local.get 4 - i32.const 1048652 - i32.add - local.get 3 - i32.store - local.get 4 - i32.const 32 - i32.add - local.tee 4 - i32.const 256 - i32.ne - br_if 0 (;@13;) - end - i32.const 1049088 - i32.const -8 - i32.const 1049088 - i32.sub - i32.const 15 - i32.and - i32.const 0 - i32.const 1049088 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 2 - i32.const 4 - i32.add - i32.const 1114112 - i32.const 1049088 - i32.sub - i32.const -56 - i32.add - local.tee 3 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 2 - i32.store offset=1048604 - local.get 3 - i32.const 1049088 - i32.add - i32.const 4 - i32.add - i32.const 56 - i32.store - end - block ;; label = @12 - block ;; label = @13 - local.get 0 - i32.const 236 - i32.gt_u - br_if 0 (;@13;) - block ;; label = @14 - i32.const 0 - i32.load offset=1048580 - local.tee 6 - i32.const 16 - local.get 0 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 0 - i32.const 11 - i32.lt_u - select - local.tee 7 - i32.const 3 - i32.shr_u - local.tee 3 - i32.shr_u - local.tee 4 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@14;) - block ;; label = @15 - block ;; label = @16 - local.get 4 - i32.const 1 - i32.and - local.get 3 - i32.or - i32.const 1 - i32.xor - local.tee 5 - i32.const 3 - i32.shl - local.tee 3 - i32.const 1048620 - i32.add - local.tee 4 - local.get 3 - i32.const 1048628 - i32.add - i32.load - local.tee 3 - i32.load offset=8 - local.tee 7 - i32.ne - br_if 0 (;@16;) - i32.const 0 - local.get 6 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 1 (;@15;) - end - local.get 4 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 4 - i32.store offset=12 - end - local.get 3 - i32.const 8 - i32.add - local.set 4 - local.get 3 - local.get 5 - i32.const 3 - i32.shl - local.tee 5 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 5 - i32.add - local.tee 3 - local.get 3 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 13 (;@1;) - end - local.get 7 - i32.const 0 - i32.load offset=1048588 - local.tee 8 - i32.le_u - br_if 1 (;@12;) - block ;; label = @14 - local.get 4 - i32.eqz - br_if 0 (;@14;) - block ;; label = @15 - block ;; label = @16 - local.get 4 - local.get 3 - i32.shl - i32.const 2 - local.get 3 - i32.shl - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.or - i32.and - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.ctz - local.tee 3 - i32.const 3 - i32.shl - local.tee 4 - i32.const 1048620 - i32.add - local.tee 5 - local.get 4 - i32.const 1048628 - i32.add - i32.load - local.tee 4 - i32.load offset=8 - local.tee 0 - i32.ne - br_if 0 (;@16;) - i32.const 0 - local.get 6 - i32.const -2 - local.get 3 - i32.rotl - i32.and - local.tee 6 - i32.store offset=1048580 - br 1 (;@15;) - end - local.get 5 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 5 - i32.store offset=12 - end - local.get 4 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - i32.const 3 - i32.shl - local.tee 3 - i32.add - local.get 3 - local.get 7 - i32.sub - local.tee 5 - i32.store - local.get 4 - local.get 7 - i32.add - local.tee 0 - local.get 5 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @15 - local.get 8 - i32.eqz - br_if 0 (;@15;) - local.get 8 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 7 - i32.const 0 - i32.load offset=1048600 - local.set 3 - block ;; label = @16 - block ;; label = @17 - local.get 6 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 9 - i32.and - br_if 0 (;@17;) - i32.const 0 - local.get 6 - local.get 9 - i32.or - i32.store offset=1048580 - local.get 7 - local.set 9 - br 1 (;@16;) - end - local.get 7 - i32.load offset=8 - local.set 9 - end - local.get 9 - local.get 3 - i32.store offset=12 - local.get 7 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 7 - i32.store offset=12 - local.get 3 - local.get 9 - i32.store offset=8 - end - local.get 4 - i32.const 8 - i32.add - local.set 4 - i32.const 0 - local.get 0 - i32.store offset=1048600 - i32.const 0 - local.get 5 - i32.store offset=1048588 - br 13 (;@1;) - end - i32.const 0 - i32.load offset=1048584 - local.tee 10 - i32.eqz - br_if 1 (;@12;) - local.get 10 - i32.const 0 - local.get 10 - i32.sub - i32.and - i32.ctz - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.tee 0 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.set 3 - local.get 0 - local.set 5 - block ;; label = @14 - loop ;; label = @15 - block ;; label = @16 - local.get 5 - i32.load offset=16 - local.tee 4 - br_if 0 (;@16;) - local.get 5 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 2 (;@14;) - end - local.get 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 5 - local.get 3 - local.get 5 - local.get 3 - i32.lt_u - local.tee 5 - select - local.set 3 - local.get 4 - local.get 0 - local.get 5 - select - local.set 0 - local.get 4 - local.set 5 - br 0 (;@15;) - end - end - local.get 0 - i32.load offset=24 - local.set 11 - block ;; label = @14 - local.get 0 - i32.load offset=12 - local.tee 9 - local.get 0 - i32.eq - br_if 0 (;@14;) - local.get 0 - i32.load offset=8 - local.tee 4 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 9 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 9 - i32.store offset=12 - br 12 (;@2;) - end - block ;; label = @14 - local.get 0 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@14;) - local.get 0 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 4 (;@10;) - local.get 0 - i32.const 16 - i32.add - local.set 5 - end - loop ;; label = @14 - local.get 5 - local.set 2 - local.get 4 - local.tee 9 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@14;) - local.get 9 - i32.const 16 - i32.add - local.set 5 - local.get 9 - i32.load offset=16 - local.tee 4 - br_if 0 (;@14;) - end - local.get 2 - i32.const 0 - i32.store - br 11 (;@2;) - end - i32.const -1 - local.set 7 - local.get 0 - i32.const -65 - i32.gt_u - br_if 0 (;@12;) - local.get 0 - i32.const 19 - i32.add - local.tee 4 - i32.const -16 - i32.and - local.set 7 - i32.const 0 - i32.load offset=1048584 - local.tee 10 - i32.eqz - br_if 0 (;@12;) - i32.const 0 - local.set 8 - block ;; label = @13 - local.get 7 - i32.const 256 - i32.lt_u - br_if 0 (;@13;) - i32.const 31 - local.set 8 - local.get 7 - i32.const 16777215 - i32.gt_u - br_if 0 (;@13;) - local.get 7 - i32.const 38 - local.get 4 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 8 - end - i32.const 0 - local.get 7 - i32.sub - local.set 3 - block ;; label = @13 - block ;; label = @14 - block ;; label = @15 - block ;; label = @16 - local.get 8 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.tee 5 - br_if 0 (;@16;) - i32.const 0 - local.set 4 - i32.const 0 - local.set 9 - br 1 (;@15;) - end - i32.const 0 - local.set 4 - local.get 7 - i32.const 0 - i32.const 25 - local.get 8 - i32.const 1 - i32.shr_u - i32.sub - local.get 8 - i32.const 31 - i32.eq - select - i32.shl - local.set 0 - i32.const 0 - local.set 9 - loop ;; label = @16 - block ;; label = @17 - local.get 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 6 - local.get 3 - i32.ge_u - br_if 0 (;@17;) - local.get 6 - local.set 3 - local.get 5 - local.set 9 - local.get 6 - br_if 0 (;@17;) - i32.const 0 - local.set 3 - local.get 5 - local.set 9 - local.get 5 - local.set 4 - br 3 (;@14;) - end - local.get 4 - local.get 5 - i32.const 20 - i32.add - i32.load - local.tee 6 - local.get 6 - local.get 5 - local.get 0 - i32.const 29 - i32.shr_u - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - i32.load - local.tee 5 - i32.eq - select - local.get 4 - local.get 6 - select - local.set 4 - local.get 0 - i32.const 1 - i32.shl - local.set 0 - local.get 5 - br_if 0 (;@16;) - end - end - block ;; label = @15 - local.get 4 - local.get 9 - i32.or - br_if 0 (;@15;) - i32.const 0 - local.set 9 - i32.const 2 - local.get 8 - i32.shl - local.tee 4 - i32.const 0 - local.get 4 - i32.sub - i32.or - local.get 10 - i32.and - local.tee 4 - i32.eqz - br_if 3 (;@12;) - local.get 4 - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.ctz - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - i32.load - local.set 4 - end - local.get 4 - i32.eqz - br_if 1 (;@13;) - end - loop ;; label = @14 - local.get 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 7 - i32.sub - local.tee 6 - local.get 3 - i32.lt_u - local.set 0 - block ;; label = @15 - local.get 4 - i32.load offset=16 - local.tee 5 - br_if 0 (;@15;) - local.get 4 - i32.const 20 - i32.add - i32.load - local.set 5 - end - local.get 6 - local.get 3 - local.get 0 - select - local.set 3 - local.get 4 - local.get 9 - local.get 0 - select - local.set 9 - local.get 5 - local.set 4 - local.get 5 - br_if 0 (;@14;) - end - end - local.get 9 - i32.eqz - br_if 0 (;@12;) - local.get 3 - i32.const 0 - i32.load offset=1048588 - local.get 7 - i32.sub - i32.ge_u - br_if 0 (;@12;) - local.get 9 - i32.load offset=24 - local.set 2 - block ;; label = @13 - local.get 9 - i32.load offset=12 - local.tee 0 - local.get 9 - i32.eq - br_if 0 (;@13;) - local.get 9 - i32.load offset=8 - local.tee 4 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 0 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 0 - i32.store offset=12 - br 10 (;@3;) - end - block ;; label = @13 - local.get 9 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@13;) - local.get 9 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 4 (;@9;) - local.get 9 - i32.const 16 - i32.add - local.set 5 - end - loop ;; label = @13 - local.get 5 - local.set 6 - local.get 4 - local.tee 0 - i32.const 20 - i32.add - local.tee 5 - i32.load - local.tee 4 - br_if 0 (;@13;) - local.get 0 - i32.const 16 - i32.add - local.set 5 - local.get 0 - i32.load offset=16 - local.tee 4 - br_if 0 (;@13;) - end - local.get 6 - i32.const 0 - i32.store - br 9 (;@3;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1048588 - local.tee 4 - local.get 7 - i32.lt_u - br_if 0 (;@12;) - i32.const 0 - i32.load offset=1048600 - local.set 3 - block ;; label = @13 - block ;; label = @14 - local.get 4 - local.get 7 - i32.sub - local.tee 5 - i32.const 16 - i32.lt_u - br_if 0 (;@14;) - local.get 3 - local.get 7 - i32.add - local.tee 0 - local.get 5 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 4 - i32.add - local.get 5 - i32.store - local.get 3 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - br 1 (;@13;) - end - local.get 3 - local.get 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 0 - i32.const 0 - local.set 5 - end - i32.const 0 - local.get 5 - i32.store offset=1048588 - i32.const 0 - local.get 0 - i32.store offset=1048600 - local.get 3 - i32.const 8 - i32.add - local.set 4 - br 11 (;@1;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1048592 - local.tee 5 - local.get 7 - i32.le_u - br_if 0 (;@12;) - local.get 2 - local.get 7 - i32.add - local.tee 4 - local.get 5 - local.get 7 - i32.sub - local.tee 3 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.get 4 - i32.store offset=1048604 - i32.const 0 - local.get 3 - i32.store offset=1048592 - local.get 2 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 2 - i32.const 8 - i32.add - local.set 4 - br 11 (;@1;) - end - block ;; label = @12 - block ;; label = @13 - i32.const 0 - i32.load offset=1049052 - i32.eqz - br_if 0 (;@13;) - i32.const 0 - i32.load offset=1049060 - local.set 3 - br 1 (;@12;) - end - i32.const 0 - i64.const -1 - i64.store offset=1049064 align=4 - i32.const 0 - i64.const 281474976776192 - i64.store offset=1049056 align=4 - i32.const 0 - local.get 1 - i32.const 12 - i32.add - i32.const -16 - i32.and - i32.const 1431655768 - i32.xor - i32.store offset=1049052 - i32.const 0 - i32.const 0 - i32.store offset=1049072 - i32.const 0 - i32.const 0 - i32.store offset=1049024 - i32.const 65536 - local.set 3 - end - i32.const 0 - local.set 4 - block ;; label = @12 - local.get 3 - local.get 7 - i32.const 71 - i32.add - local.tee 8 - i32.add - local.tee 0 - i32.const 0 - local.get 3 - i32.sub - local.tee 6 - i32.and - local.tee 9 - local.get 7 - i32.gt_u - br_if 0 (;@12;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 11 (;@1;) - end - block ;; label = @12 - i32.const 0 - i32.load offset=1049020 - local.tee 4 - i32.eqz - br_if 0 (;@12;) - block ;; label = @13 - i32.const 0 - i32.load offset=1049012 - local.tee 3 - local.get 9 - i32.add - local.tee 10 - local.get 3 - i32.le_u - br_if 0 (;@13;) - local.get 10 - local.get 4 - i32.le_u - br_if 1 (;@12;) - end - i32.const 0 - local.set 4 - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 11 (;@1;) - end - i32.const 0 - i32.load8_u offset=1049024 - i32.const 4 - i32.and - br_if 5 (;@6;) - block ;; label = @12 - block ;; label = @13 - block ;; label = @14 - local.get 2 - i32.eqz - br_if 0 (;@14;) - i32.const 1049028 - local.set 4 - loop ;; label = @15 - block ;; label = @16 - local.get 4 - i32.load - local.tee 3 - local.get 2 - i32.gt_u - br_if 0 (;@16;) - local.get 3 - local.get 4 - i32.load offset=4 - i32.add - local.get 2 - i32.gt_u - br_if 3 (;@13;) - end - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@15;) - end - end - i32.const 0 - call $sbrk - local.tee 0 - i32.const -1 - i32.eq - br_if 6 (;@7;) - local.get 9 - local.set 6 - block ;; label = @14 - i32.const 0 - i32.load offset=1049056 - local.tee 4 - i32.const -1 - i32.add - local.tee 3 - local.get 0 - i32.and - i32.eqz - br_if 0 (;@14;) - local.get 9 - local.get 0 - i32.sub - local.get 3 - local.get 0 - i32.add - i32.const 0 - local.get 4 - i32.sub - i32.and - i32.add - local.set 6 - end - local.get 6 - local.get 7 - i32.le_u - br_if 6 (;@7;) - local.get 6 - i32.const 2147483646 - i32.gt_u - br_if 6 (;@7;) - block ;; label = @14 - i32.const 0 - i32.load offset=1049020 - local.tee 4 - i32.eqz - br_if 0 (;@14;) - i32.const 0 - i32.load offset=1049012 - local.tee 3 - local.get 6 - i32.add - local.tee 5 - local.get 3 - i32.le_u - br_if 7 (;@7;) - local.get 5 - local.get 4 - i32.gt_u - br_if 7 (;@7;) - end - local.get 6 - call $sbrk - local.tee 4 - local.get 0 - i32.ne - br_if 1 (;@12;) - br 8 (;@5;) - end - local.get 0 - local.get 5 - i32.sub - local.get 6 - i32.and - local.tee 6 - i32.const 2147483646 - i32.gt_u - br_if 5 (;@7;) - local.get 6 - call $sbrk - local.tee 0 - local.get 4 - i32.load - local.get 4 - i32.load offset=4 - i32.add - i32.eq - br_if 4 (;@8;) - local.get 0 - local.set 4 - end - block ;; label = @12 - local.get 4 - i32.const -1 - i32.eq - br_if 0 (;@12;) - local.get 7 - i32.const 72 - i32.add - local.get 6 - i32.le_u - br_if 0 (;@12;) - block ;; label = @13 - local.get 8 - local.get 6 - i32.sub - i32.const 0 - i32.load offset=1049060 - local.tee 3 - i32.add - i32.const 0 - local.get 3 - i32.sub - i32.and - local.tee 3 - i32.const 2147483646 - i32.le_u - br_if 0 (;@13;) - local.get 4 - local.set 0 - br 8 (;@5;) - end - block ;; label = @13 - local.get 3 - call $sbrk - i32.const -1 - i32.eq - br_if 0 (;@13;) - local.get 3 - local.get 6 - i32.add - local.set 6 - local.get 4 - local.set 0 - br 8 (;@5;) - end - i32.const 0 - local.get 6 - i32.sub - call $sbrk - drop - br 5 (;@7;) - end - local.get 4 - local.set 0 - local.get 4 - i32.const -1 - i32.ne - br_if 6 (;@5;) - br 4 (;@7;) - end - unreachable - unreachable - end - i32.const 0 - local.set 9 - br 7 (;@2;) - end - i32.const 0 - local.set 0 - br 5 (;@3;) - end - local.get 0 - i32.const -1 - i32.ne - br_if 2 (;@5;) - end - i32.const 0 - i32.const 0 - i32.load offset=1049024 - i32.const 4 - i32.or - i32.store offset=1049024 - end - local.get 9 - i32.const 2147483646 - i32.gt_u - br_if 1 (;@4;) - local.get 9 - call $sbrk - local.set 0 - i32.const 0 - call $sbrk - local.set 4 - local.get 0 - i32.const -1 - i32.eq - br_if 1 (;@4;) - local.get 4 - i32.const -1 - i32.eq - br_if 1 (;@4;) - local.get 0 - local.get 4 - i32.ge_u - br_if 1 (;@4;) - local.get 4 - local.get 0 - i32.sub - local.tee 6 - local.get 7 - i32.const 56 - i32.add - i32.le_u - br_if 1 (;@4;) - end - i32.const 0 - i32.const 0 - i32.load offset=1049012 - local.get 6 - i32.add - local.tee 4 - i32.store offset=1049012 - block ;; label = @5 - local.get 4 - i32.const 0 - i32.load offset=1049016 - i32.le_u - br_if 0 (;@5;) - i32.const 0 - local.get 4 - i32.store offset=1049016 - end - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - i32.const 0 - i32.load offset=1048604 - local.tee 3 - i32.eqz - br_if 0 (;@8;) - i32.const 1049028 - local.set 4 - loop ;; label = @9 - local.get 0 - local.get 4 - i32.load - local.tee 5 - local.get 4 - i32.load offset=4 - local.tee 9 - i32.add - i32.eq - br_if 2 (;@7;) - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@9;) - br 3 (;@6;) - end - end - block ;; label = @8 - block ;; label = @9 - i32.const 0 - i32.load offset=1048596 - local.tee 4 - i32.eqz - br_if 0 (;@9;) - local.get 0 - local.get 4 - i32.ge_u - br_if 1 (;@8;) - end - i32.const 0 - local.get 0 - i32.store offset=1048596 - end - i32.const 0 - local.set 4 - i32.const 0 - local.get 6 - i32.store offset=1049032 - i32.const 0 - local.get 0 - i32.store offset=1049028 - i32.const 0 - i32.const -1 - i32.store offset=1048612 - i32.const 0 - i32.const 0 - i32.load offset=1049052 - i32.store offset=1048616 - i32.const 0 - i32.const 0 - i32.store offset=1049040 - loop ;; label = @8 - local.get 4 - i32.const 1048640 - i32.add - local.get 4 - i32.const 1048628 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 4 - i32.const 1048620 - i32.add - local.tee 5 - i32.store - local.get 4 - i32.const 1048632 - i32.add - local.get 5 - i32.store - local.get 4 - i32.const 1048648 - i32.add - local.get 4 - i32.const 1048636 - i32.add - local.tee 5 - i32.store - local.get 5 - local.get 3 - i32.store - local.get 4 - i32.const 1048656 - i32.add - local.get 4 - i32.const 1048644 - i32.add - local.tee 3 - i32.store - local.get 3 - local.get 5 - i32.store - local.get 4 - i32.const 1048652 - i32.add - local.get 3 - i32.store - local.get 4 - i32.const 32 - i32.add - local.tee 4 - i32.const 256 - i32.ne - br_if 0 (;@8;) - end - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 3 - local.get 6 - i32.const -56 - i32.add - local.tee 5 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 3 - i32.store offset=1048604 - local.get 0 - local.get 5 - i32.add - i32.const 56 - i32.store offset=4 - br 2 (;@5;) - end - local.get 4 - i32.load8_u offset=12 - i32.const 8 - i32.and - br_if 0 (;@6;) - local.get 3 - local.get 5 - i32.lt_u - br_if 0 (;@6;) - local.get 3 - local.get 0 - i32.ge_u - br_if 0 (;@6;) - local.get 3 - i32.const -8 - local.get 3 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 3 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 5 - i32.add - local.tee 0 - i32.const 0 - i32.load offset=1048592 - local.get 6 - i32.add - local.tee 2 - local.get 5 - i32.sub - local.tee 5 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 9 - local.get 6 - i32.add - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 5 - i32.store offset=1048592 - i32.const 0 - local.get 0 - i32.store offset=1048604 - local.get 3 - local.get 2 - i32.add - i32.const 56 - i32.store offset=4 - br 1 (;@5;) - end - block ;; label = @6 - local.get 0 - i32.const 0 - i32.load offset=1048596 - local.tee 9 - i32.ge_u - br_if 0 (;@6;) - i32.const 0 - local.get 0 - i32.store offset=1048596 - local.get 0 - local.set 9 - end - local.get 0 - local.get 6 - i32.add - local.set 5 - i32.const 1049028 - local.set 4 - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - block ;; label = @9 - block ;; label = @10 - block ;; label = @11 - block ;; label = @12 - loop ;; label = @13 - local.get 4 - i32.load - local.get 5 - i32.eq - br_if 1 (;@12;) - local.get 4 - i32.load offset=8 - local.tee 4 - br_if 0 (;@13;) - br 2 (;@11;) - end - end - local.get 4 - i32.load8_u offset=12 - i32.const 8 - i32.and - i32.eqz - br_if 1 (;@10;) - end - i32.const 1049028 - local.set 4 - loop ;; label = @11 - block ;; label = @12 - local.get 4 - i32.load - local.tee 5 - local.get 3 - i32.gt_u - br_if 0 (;@12;) - local.get 5 - local.get 4 - i32.load offset=4 - i32.add - local.tee 5 - local.get 3 - i32.gt_u - br_if 3 (;@9;) - end - local.get 4 - i32.load offset=8 - local.set 4 - br 0 (;@11;) - end - end - local.get 4 - local.get 0 - i32.store - local.get 4 - local.get 4 - i32.load offset=4 - local.get 6 - i32.add - i32.store offset=4 - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - i32.add - local.tee 2 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 5 - i32.const -8 - local.get 5 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 5 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - i32.add - local.tee 6 - local.get 2 - local.get 7 - i32.add - local.tee 7 - i32.sub - local.set 4 - block ;; label = @10 - local.get 6 - local.get 3 - i32.ne - br_if 0 (;@10;) - i32.const 0 - local.get 7 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 4 - i32.add - local.tee 4 - i32.store offset=1048592 - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - br 3 (;@7;) - end - block ;; label = @10 - local.get 6 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@10;) - i32.const 0 - local.get 7 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 4 - i32.add - local.tee 4 - i32.store offset=1048588 - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 7 - local.get 4 - i32.add - local.get 4 - i32.store - br 3 (;@7;) - end - block ;; label = @10 - local.get 6 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 1 - i32.ne - br_if 0 (;@10;) - local.get 3 - i32.const -8 - i32.and - local.set 8 - block ;; label = @11 - block ;; label = @12 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@12;) - local.get 6 - i32.load offset=8 - local.tee 5 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 9 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 0 - i32.eq - drop - block ;; label = @13 - local.get 6 - i32.load offset=12 - local.tee 3 - local.get 5 - i32.ne - br_if 0 (;@13;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 9 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@11;) - end - local.get 3 - local.get 0 - i32.eq - drop - local.get 3 - local.get 5 - i32.store offset=8 - local.get 5 - local.get 3 - i32.store offset=12 - br 1 (;@11;) - end - local.get 6 - i32.load offset=24 - local.set 10 - block ;; label = @12 - block ;; label = @13 - local.get 6 - i32.load offset=12 - local.tee 0 - local.get 6 - i32.eq - br_if 0 (;@13;) - local.get 6 - i32.load offset=8 - local.tee 3 - local.get 9 - i32.lt_u - drop - local.get 0 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 0 - i32.store offset=12 - br 1 (;@12;) - end - block ;; label = @13 - local.get 6 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - local.get 6 - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - i32.const 0 - local.set 0 - br 1 (;@12;) - end - loop ;; label = @13 - local.get 3 - local.set 9 - local.get 5 - local.tee 0 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 5 - br_if 0 (;@13;) - local.get 0 - i32.const 16 - i32.add - local.set 3 - local.get 0 - i32.load offset=16 - local.tee 5 - br_if 0 (;@13;) - end - local.get 9 - i32.const 0 - i32.store - end - local.get 10 - i32.eqz - br_if 0 (;@11;) - block ;; label = @12 - block ;; label = @13 - local.get 6 - local.get 6 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@13;) - local.get 3 - local.get 0 - i32.store - local.get 0 - br_if 1 (;@12;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@11;) - end - local.get 10 - i32.const 16 - i32.const 20 - local.get 10 - i32.load offset=16 - local.get 6 - i32.eq - select - i32.add - local.get 0 - i32.store - local.get 0 - i32.eqz - br_if 1 (;@11;) - end - local.get 0 - local.get 10 - i32.store offset=24 - block ;; label = @12 - local.get 6 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@12;) - local.get 0 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 0 - i32.store offset=24 - end - local.get 6 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 0 (;@11;) - local.get 0 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 0 - i32.store offset=24 - end - local.get 8 - local.get 4 - i32.add - local.set 4 - local.get 6 - local.get 8 - i32.add - local.tee 6 - i32.load offset=4 - local.set 3 - end - local.get 6 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 7 - local.get 4 - i32.add - local.get 4 - i32.store - local.get 7 - local.get 4 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @10 - local.get 4 - i32.const 255 - i32.gt_u - br_if 0 (;@10;) - local.get 4 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 3 - block ;; label = @11 - block ;; label = @12 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 4 - i32.const 3 - i32.shr_u - i32.shl - local.tee 4 - i32.and - br_if 0 (;@12;) - i32.const 0 - local.get 5 - local.get 4 - i32.or - i32.store offset=1048580 - local.get 3 - local.set 4 - br 1 (;@11;) - end - local.get 3 - i32.load offset=8 - local.set 4 - end - local.get 4 - local.get 7 - i32.store offset=12 - local.get 3 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 3 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - br 3 (;@7;) - end - i32.const 31 - local.set 3 - block ;; label = @10 - local.get 4 - i32.const 16777215 - i32.gt_u - br_if 0 (;@10;) - local.get 4 - i32.const 38 - local.get 4 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 7 - local.get 3 - i32.store offset=28 - local.get 7 - i64.const 0 - i64.store offset=16 align=4 - local.get 3 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @10 - i32.const 0 - i32.load offset=1048584 - local.tee 0 - i32.const 1 - local.get 3 - i32.shl - local.tee 9 - i32.and - br_if 0 (;@10;) - local.get 5 - local.get 7 - i32.store - i32.const 0 - local.get 0 - local.get 9 - i32.or - i32.store offset=1048584 - local.get 7 - local.get 5 - i32.store offset=24 - local.get 7 - local.get 7 - i32.store offset=8 - local.get 7 - local.get 7 - i32.store offset=12 - br 3 (;@7;) - end - local.get 4 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 5 - i32.load - local.set 0 - loop ;; label = @10 - local.get 0 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 4 - i32.eq - br_if 2 (;@8;) - local.get 3 - i32.const 29 - i32.shr_u - local.set 0 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 5 - local.get 0 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 9 - i32.load - local.tee 0 - br_if 0 (;@10;) - end - local.get 9 - local.get 7 - i32.store - local.get 7 - local.get 5 - i32.store offset=24 - local.get 7 - local.get 7 - i32.store offset=12 - local.get 7 - local.get 7 - i32.store offset=8 - br 2 (;@7;) - end - local.get 0 - i32.const -8 - local.get 0 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 0 - i32.const 8 - i32.add - i32.const 15 - i32.and - select - local.tee 4 - i32.add - local.tee 2 - local.get 6 - i32.const -56 - i32.add - local.tee 9 - local.get 4 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 9 - i32.add - i32.const 56 - i32.store offset=4 - local.get 3 - local.get 5 - i32.const 55 - local.get 5 - i32.sub - i32.const 15 - i32.and - i32.const 0 - local.get 5 - i32.const -55 - i32.add - i32.const 15 - i32.and - select - i32.add - i32.const -63 - i32.add - local.tee 9 - local.get 9 - local.get 3 - i32.const 16 - i32.add - i32.lt_u - select - local.tee 9 - i32.const 35 - i32.store offset=4 - i32.const 0 - i32.const 0 - i32.load offset=1049068 - i32.store offset=1048608 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 2 - i32.store offset=1048604 - local.get 9 - i32.const 16 - i32.add - i32.const 0 - i64.load offset=1049036 align=4 - i64.store align=4 - local.get 9 - i32.const 0 - i64.load offset=1049028 align=4 - i64.store offset=8 align=4 - i32.const 0 - local.get 9 - i32.const 8 - i32.add - i32.store offset=1049036 - i32.const 0 - local.get 6 - i32.store offset=1049032 - i32.const 0 - local.get 0 - i32.store offset=1049028 - i32.const 0 - i32.const 0 - i32.store offset=1049040 - local.get 9 - i32.const 36 - i32.add - local.set 4 - loop ;; label = @9 - local.get 4 - i32.const 7 - i32.store - local.get 4 - i32.const 4 - i32.add - local.tee 4 - local.get 5 - i32.lt_u - br_if 0 (;@9;) - end - local.get 9 - local.get 3 - i32.eq - br_if 3 (;@5;) - local.get 9 - local.get 9 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - local.get 9 - local.get 9 - local.get 3 - i32.sub - local.tee 0 - i32.store - local.get 3 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - block ;; label = @9 - local.get 0 - i32.const 255 - i32.gt_u - br_if 0 (;@9;) - local.get 0 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 4 - block ;; label = @10 - block ;; label = @11 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 0 - i32.const 3 - i32.shr_u - i32.shl - local.tee 0 - i32.and - br_if 0 (;@11;) - i32.const 0 - local.get 5 - local.get 0 - i32.or - i32.store offset=1048580 - local.get 4 - local.set 5 - br 1 (;@10;) - end - local.get 4 - i32.load offset=8 - local.set 5 - end - local.get 5 - local.get 3 - i32.store offset=12 - local.get 4 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 4 - i32.store offset=12 - local.get 3 - local.get 5 - i32.store offset=8 - br 4 (;@5;) - end - i32.const 31 - local.set 4 - block ;; label = @9 - local.get 0 - i32.const 16777215 - i32.gt_u - br_if 0 (;@9;) - local.get 0 - i32.const 38 - local.get 0 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 4 - end - local.get 3 - local.get 4 - i32.store offset=28 - local.get 3 - i64.const 0 - i64.store offset=16 align=4 - local.get 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @9 - i32.const 0 - i32.load offset=1048584 - local.tee 9 - i32.const 1 - local.get 4 - i32.shl - local.tee 6 - i32.and - br_if 0 (;@9;) - local.get 5 - local.get 3 - i32.store - i32.const 0 - local.get 9 - local.get 6 - i32.or - i32.store offset=1048584 - local.get 3 - local.get 5 - i32.store offset=24 - local.get 3 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 3 - i32.store offset=12 - br 4 (;@5;) - end - local.get 0 - i32.const 0 - i32.const 25 - local.get 4 - i32.const 1 - i32.shr_u - i32.sub - local.get 4 - i32.const 31 - i32.eq - select - i32.shl - local.set 4 - local.get 5 - i32.load - local.set 9 - loop ;; label = @9 - local.get 9 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 0 - i32.eq - br_if 3 (;@6;) - local.get 4 - i32.const 29 - i32.shr_u - local.set 9 - local.get 4 - i32.const 1 - i32.shl - local.set 4 - local.get 5 - local.get 9 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 6 - i32.load - local.tee 9 - br_if 0 (;@9;) - end - local.get 6 - local.get 3 - i32.store - local.get 3 - local.get 5 - i32.store offset=24 - local.get 3 - local.get 3 - i32.store offset=12 - local.get 3 - local.get 3 - i32.store offset=8 - br 3 (;@5;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 7 - i32.store offset=12 - local.get 5 - local.get 7 - i32.store offset=8 - local.get 7 - i32.const 0 - i32.store offset=24 - local.get 7 - local.get 5 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - end - local.get 2 - i32.const 8 - i32.add - local.set 4 - br 5 (;@1;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.store offset=12 - local.get 5 - local.get 3 - i32.store offset=8 - local.get 3 - i32.const 0 - i32.store offset=24 - local.get 3 - local.get 5 - i32.store offset=12 - local.get 3 - local.get 4 - i32.store offset=8 - end - i32.const 0 - i32.load offset=1048592 - local.tee 4 - local.get 7 - i32.le_u - br_if 0 (;@4;) - i32.const 0 - i32.load offset=1048604 - local.tee 3 - local.get 7 - i32.add - local.tee 5 - local.get 4 - local.get 7 - i32.sub - local.tee 4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.get 4 - i32.store offset=1048592 - i32.const 0 - local.get 5 - i32.store offset=1048604 - local.get 3 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 3 - i32.const 8 - i32.add - local.set 4 - br 3 (;@1;) - end - i32.const 0 - local.set 4 - i32.const 0 - i32.const 48 - i32.store offset=1049076 - br 2 (;@1;) - end - block ;; label = @3 - local.get 2 - i32.eqz - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 9 - local.get 9 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 4 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 4 - local.get 0 - i32.store - local.get 0 - br_if 1 (;@4;) - i32.const 0 - local.get 10 - i32.const -2 - local.get 5 - i32.rotl - i32.and - local.tee 10 - i32.store offset=1048584 - br 2 (;@3;) - end - local.get 2 - i32.const 16 - i32.const 20 - local.get 2 - i32.load offset=16 - local.get 9 - i32.eq - select - i32.add - local.get 0 - i32.store - local.get 0 - i32.eqz - br_if 1 (;@3;) - end - local.get 0 - local.get 2 - i32.store offset=24 - block ;; label = @4 - local.get 9 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 0 (;@4;) - local.get 0 - local.get 4 - i32.store offset=16 - local.get 4 - local.get 0 - i32.store offset=24 - end - local.get 9 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@3;) - local.get 0 - i32.const 20 - i32.add - local.get 4 - i32.store - local.get 4 - local.get 0 - i32.store offset=24 - end - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 15 - i32.gt_u - br_if 0 (;@4;) - local.get 9 - local.get 3 - local.get 7 - i32.add - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 9 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 1 (;@3;) - end - local.get 9 - local.get 7 - i32.add - local.tee 0 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 9 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @4 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 3 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 4 - block ;; label = @5 - block ;; label = @6 - i32.const 0 - i32.load offset=1048580 - local.tee 5 - i32.const 1 - local.get 3 - i32.const 3 - i32.shr_u - i32.shl - local.tee 3 - i32.and - br_if 0 (;@6;) - i32.const 0 - local.get 5 - local.get 3 - i32.or - i32.store offset=1048580 - local.get 4 - local.set 3 - br 1 (;@5;) - end - local.get 4 - i32.load offset=8 - local.set 3 - end - local.get 3 - local.get 0 - i32.store offset=12 - local.get 4 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 4 - i32.store offset=12 - local.get 0 - local.get 3 - i32.store offset=8 - br 1 (;@3;) - end - i32.const 31 - local.set 4 - block ;; label = @4 - local.get 3 - i32.const 16777215 - i32.gt_u - br_if 0 (;@4;) - local.get 3 - i32.const 38 - local.get 3 - i32.const 8 - i32.shr_u - i32.clz - local.tee 4 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 4 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 4 - end - local.get 0 - local.get 4 - i32.store offset=28 - local.get 0 - i64.const 0 - i64.store offset=16 align=4 - local.get 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 5 - block ;; label = @4 - local.get 10 - i32.const 1 - local.get 4 - i32.shl - local.tee 7 - i32.and - br_if 0 (;@4;) - local.get 5 - local.get 0 - i32.store - i32.const 0 - local.get 10 - local.get 7 - i32.or - i32.store offset=1048584 - local.get 0 - local.get 5 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 0 - i32.store offset=12 - br 1 (;@3;) - end - local.get 3 - i32.const 0 - i32.const 25 - local.get 4 - i32.const 1 - i32.shr_u - i32.sub - local.get 4 - i32.const 31 - i32.eq - select - i32.shl - local.set 4 - local.get 5 - i32.load - local.set 7 - block ;; label = @4 - loop ;; label = @5 - local.get 7 - local.tee 5 - i32.load offset=4 - i32.const -8 - i32.and - local.get 3 - i32.eq - br_if 1 (;@4;) - local.get 4 - i32.const 29 - i32.shr_u - local.set 7 - local.get 4 - i32.const 1 - i32.shl - local.set 4 - local.get 5 - local.get 7 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 6 - i32.load - local.tee 7 - br_if 0 (;@5;) - end - local.get 6 - local.get 0 - i32.store - local.get 0 - local.get 5 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=12 - local.get 0 - local.get 0 - i32.store offset=8 - br 1 (;@3;) - end - local.get 5 - i32.load offset=8 - local.tee 4 - local.get 0 - i32.store offset=12 - local.get 5 - local.get 0 - i32.store offset=8 - local.get 0 - i32.const 0 - i32.store offset=24 - local.get 0 - local.get 5 - i32.store offset=12 - local.get 0 - local.get 4 - i32.store offset=8 - end - local.get 9 - i32.const 8 - i32.add - local.set 4 - br 1 (;@1;) - end - block ;; label = @2 - local.get 11 - i32.eqz - br_if 0 (;@2;) - block ;; label = @3 - block ;; label = @4 - local.get 0 - local.get 0 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 4 - i32.load - i32.ne - br_if 0 (;@4;) - local.get 4 - local.get 9 - i32.store - local.get 9 - br_if 1 (;@3;) - i32.const 0 - local.get 10 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@2;) - end - local.get 11 - i32.const 16 - i32.const 20 - local.get 11 - i32.load offset=16 - local.get 0 - i32.eq - select - i32.add - local.get 9 - i32.store - local.get 9 - i32.eqz - br_if 1 (;@2;) - end - local.get 9 - local.get 11 - i32.store offset=24 - block ;; label = @3 - local.get 0 - i32.load offset=16 - local.tee 4 - i32.eqz - br_if 0 (;@3;) - local.get 9 - local.get 4 - i32.store offset=16 - local.get 4 - local.get 9 - i32.store offset=24 - end - local.get 0 - i32.const 20 - i32.add - i32.load - local.tee 4 - i32.eqz - br_if 0 (;@2;) - local.get 9 - i32.const 20 - i32.add - local.get 4 - i32.store - local.get 4 - local.get 9 - i32.store offset=24 - end - block ;; label = @2 - block ;; label = @3 - local.get 3 - i32.const 15 - i32.gt_u - br_if 0 (;@3;) - local.get 0 - local.get 3 - local.get 7 - i32.add - local.tee 4 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 4 - i32.add - local.tee 4 - local.get 4 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - br 1 (;@2;) - end - local.get 0 - local.get 7 - i32.add - local.tee 5 - local.get 3 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 7 - i32.const 3 - i32.or - i32.store offset=4 - local.get 5 - local.get 3 - i32.add - local.get 3 - i32.store - block ;; label = @3 - local.get 8 - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 7 - i32.const 0 - i32.load offset=1048600 - local.set 4 - block ;; label = @4 - block ;; label = @5 - i32.const 1 - local.get 8 - i32.const 3 - i32.shr_u - i32.shl - local.tee 9 - local.get 6 - i32.and - br_if 0 (;@5;) - i32.const 0 - local.get 9 - local.get 6 - i32.or - i32.store offset=1048580 - local.get 7 - local.set 9 - br 1 (;@4;) - end - local.get 7 - i32.load offset=8 - local.set 9 - end - local.get 9 - local.get 4 - i32.store offset=12 - local.get 7 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 7 - i32.store offset=12 - local.get 4 - local.get 9 - i32.store offset=8 - end - i32.const 0 - local.get 5 - i32.store offset=1048600 - i32.const 0 - local.get 3 - i32.store offset=1048588 - end - local.get 0 - i32.const 8 - i32.add - local.set 4 - end - local.get 1 - i32.const 16 - i32.add - global.set $__stack_pointer - local.get 4 - ) - (func $free (;6;) (type 3) (param i32) - local.get 0 - call $dlfree - ) - (func $dlfree (;7;) (type 3) (param i32) - (local i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 0 - i32.eqz - br_if 0 (;@1;) - local.get 0 - i32.const -8 - i32.add - local.tee 1 - local.get 0 - i32.const -4 - i32.add - i32.load - local.tee 2 - i32.const -8 - i32.and - local.tee 0 - i32.add - local.set 3 - block ;; label = @2 - local.get 2 - i32.const 1 - i32.and - br_if 0 (;@2;) - local.get 2 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 1 - local.get 1 - i32.load - local.tee 2 - i32.sub - local.tee 1 - i32.const 0 - i32.load offset=1048596 - local.tee 4 - i32.lt_u - br_if 1 (;@1;) - local.get 2 - local.get 0 - i32.add - local.set 0 - block ;; label = @3 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.eq - br_if 0 (;@3;) - block ;; label = @4 - local.get 2 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 1 - i32.load offset=8 - local.tee 4 - local.get 2 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @5 - local.get 1 - i32.load offset=12 - local.tee 2 - local.get 4 - i32.ne - br_if 0 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 3 (;@2;) - end - local.get 2 - local.get 6 - i32.eq - drop - local.get 2 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 2 - i32.store offset=12 - br 2 (;@2;) - end - local.get 1 - i32.load offset=24 - local.set 7 - block ;; label = @4 - block ;; label = @5 - local.get 1 - i32.load offset=12 - local.tee 6 - local.get 1 - i32.eq - br_if 0 (;@5;) - local.get 1 - i32.load offset=8 - local.tee 2 - local.get 4 - i32.lt_u - drop - local.get 6 - local.get 2 - i32.store offset=8 - local.get 2 - local.get 6 - i32.store offset=12 - br 1 (;@4;) - end - block ;; label = @5 - local.get 1 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - local.get 1 - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - i32.const 0 - local.set 6 - br 1 (;@4;) - end - loop ;; label = @5 - local.get 2 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@5;) - local.get 6 - i32.const 16 - i32.add - local.set 2 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@5;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 1 - local.get 1 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 2 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 2 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@4;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 3 (;@2;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 1 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 2 (;@2;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @4 - local.get 1 - i32.load offset=16 - local.tee 2 - i32.eqz - br_if 0 (;@4;) - local.get 6 - local.get 2 - i32.store offset=16 - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 1 - i32.load offset=20 - local.tee 2 - i32.eqz - br_if 1 (;@2;) - local.get 6 - i32.const 20 - i32.add - local.get 2 - i32.store - local.get 2 - local.get 6 - i32.store offset=24 - br 1 (;@2;) - end - local.get 3 - i32.load offset=4 - local.tee 2 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 0 (;@2;) - local.get 3 - local.get 2 - i32.const -2 - i32.and - i32.store offset=4 - i32.const 0 - local.get 0 - i32.store offset=1048588 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - return - end - local.get 1 - local.get 3 - i32.ge_u - br_if 0 (;@1;) - local.get 3 - i32.load offset=4 - local.tee 2 - i32.const 1 - i32.and - i32.eqz - br_if 0 (;@1;) - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 2 - i32.and - br_if 0 (;@3;) - block ;; label = @4 - local.get 3 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 1 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 0 - i32.add - local.tee 0 - i32.store offset=1048592 - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 3 (;@1;) - i32.const 0 - i32.const 0 - i32.store offset=1048588 - i32.const 0 - i32.const 0 - i32.store offset=1048600 - return - end - block ;; label = @4 - local.get 3 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 1 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 0 - i32.add - local.tee 0 - i32.store offset=1048588 - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - return - end - local.get 2 - i32.const -8 - i32.and - local.get 0 - i32.add - local.set 0 - block ;; label = @4 - block ;; label = @5 - local.get 2 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 3 - i32.load offset=8 - local.tee 4 - local.get 2 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @6 - local.get 3 - i32.load offset=12 - local.tee 2 - local.get 4 - i32.ne - br_if 0 (;@6;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@4;) - end - local.get 2 - local.get 6 - i32.eq - drop - local.get 2 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 2 - i32.store offset=12 - br 1 (;@4;) - end - local.get 3 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.load offset=12 - local.tee 6 - local.get 3 - i32.eq - br_if 0 (;@6;) - local.get 3 - i32.load offset=8 - local.tee 2 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 2 - i32.store offset=8 - local.get 2 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 3 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 3 - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 2 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 2 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 2 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 0 (;@4;) - block ;; label = @5 - block ;; label = @6 - local.get 3 - local.get 3 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 2 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 2 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@4;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 3 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 1 (;@4;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 3 - i32.load offset=16 - local.tee 2 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 2 - i32.store offset=16 - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 3 - i32.load offset=20 - local.tee 2 - i32.eqz - br_if 0 (;@4;) - local.get 6 - i32.const 20 - i32.add - local.get 2 - i32.store - local.get 2 - local.get 6 - i32.store offset=24 - end - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 1 (;@2;) - i32.const 0 - local.get 0 - i32.store offset=1048588 - return - end - local.get 3 - local.get 2 - i32.const -2 - i32.and - i32.store offset=4 - local.get 1 - local.get 0 - i32.add - local.get 0 - i32.store - local.get 1 - local.get 0 - i32.const 1 - i32.or - i32.store offset=4 - end - block ;; label = @2 - local.get 0 - i32.const 255 - i32.gt_u - br_if 0 (;@2;) - local.get 0 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 2 - block ;; label = @3 - block ;; label = @4 - i32.const 0 - i32.load offset=1048580 - local.tee 4 - i32.const 1 - local.get 0 - i32.const 3 - i32.shr_u - i32.shl - local.tee 0 - i32.and - br_if 0 (;@4;) - i32.const 0 - local.get 4 - local.get 0 - i32.or - i32.store offset=1048580 - local.get 2 - local.set 0 - br 1 (;@3;) - end - local.get 2 - i32.load offset=8 - local.set 0 - end - local.get 0 - local.get 1 - i32.store offset=12 - local.get 2 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 2 - i32.store offset=12 - local.get 1 - local.get 0 - i32.store offset=8 - return - end - i32.const 31 - local.set 2 - block ;; label = @2 - local.get 0 - i32.const 16777215 - i32.gt_u - br_if 0 (;@2;) - local.get 0 - i32.const 38 - local.get 0 - i32.const 8 - i32.shr_u - i32.clz - local.tee 2 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 2 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 2 - end - local.get 1 - local.get 2 - i32.store offset=28 - local.get 1 - i64.const 0 - i64.store offset=16 align=4 - local.get 2 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 4 - block ;; label = @2 - block ;; label = @3 - i32.const 0 - i32.load offset=1048584 - local.tee 6 - i32.const 1 - local.get 2 - i32.shl - local.tee 3 - i32.and - br_if 0 (;@3;) - local.get 4 - local.get 1 - i32.store - i32.const 0 - local.get 6 - local.get 3 - i32.or - i32.store offset=1048584 - local.get 1 - local.get 4 - i32.store offset=24 - local.get 1 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 1 - i32.store offset=12 - br 1 (;@2;) - end - local.get 0 - i32.const 0 - i32.const 25 - local.get 2 - i32.const 1 - i32.shr_u - i32.sub - local.get 2 - i32.const 31 - i32.eq - select - i32.shl - local.set 2 - local.get 4 - i32.load - local.set 6 - block ;; label = @3 - loop ;; label = @4 - local.get 6 - local.tee 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 0 - i32.eq - br_if 1 (;@3;) - local.get 2 - i32.const 29 - i32.shr_u - local.set 6 - local.get 2 - i32.const 1 - i32.shl - local.set 2 - local.get 4 - local.get 6 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 6 - br_if 0 (;@4;) - end - local.get 3 - local.get 1 - i32.store - local.get 1 - local.get 4 - i32.store offset=24 - local.get 1 - local.get 1 - i32.store offset=12 - local.get 1 - local.get 1 - i32.store offset=8 - br 1 (;@2;) - end - local.get 4 - i32.load offset=8 - local.tee 0 - local.get 1 - i32.store offset=12 - local.get 4 - local.get 1 - i32.store offset=8 - local.get 1 - i32.const 0 - i32.store offset=24 - local.get 1 - local.get 4 - i32.store offset=12 - local.get 1 - local.get 0 - i32.store offset=8 - end - i32.const 0 - i32.const 0 - i32.load offset=1048612 - i32.const -1 - i32.add - local.tee 1 - i32.const -1 - local.get 1 - select - i32.store offset=1048612 - end - ) - (func $realloc (;8;) (type 0) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 0 - br_if 0 (;@1;) - local.get 1 - call $dlmalloc - return - end - block ;; label = @1 - local.get 1 - i32.const -64 - i32.lt_u - br_if 0 (;@1;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const 0 - return - end - i32.const 16 - local.get 1 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.set 2 - local.get 0 - i32.const -4 - i32.add - local.tee 3 - i32.load - local.tee 4 - i32.const -8 - i32.and - local.set 5 - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 4 - i32.const 3 - i32.and - br_if 0 (;@3;) - local.get 2 - i32.const 256 - i32.lt_u - br_if 1 (;@2;) - local.get 5 - local.get 2 - i32.const 4 - i32.or - i32.lt_u - br_if 1 (;@2;) - local.get 5 - local.get 2 - i32.sub - i32.const 0 - i32.load offset=1049060 - i32.const 1 - i32.shl - i32.le_u - br_if 2 (;@1;) - br 1 (;@2;) - end - local.get 0 - i32.const -8 - i32.add - local.tee 6 - local.get 5 - i32.add - local.set 7 - block ;; label = @3 - local.get 5 - local.get 2 - i32.lt_u - br_if 0 (;@3;) - local.get 5 - local.get 2 - i32.sub - local.tee 1 - i32.const 16 - i32.lt_u - br_if 2 (;@1;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 2 - local.get 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 7 - local.get 7 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 1 - call $dispose_chunk - local.get 0 - return - end - block ;; label = @3 - local.get 7 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1048592 - local.get 5 - i32.add - local.tee 5 - local.get 2 - i32.le_u - br_if 1 (;@2;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - i32.const 0 - local.get 6 - local.get 2 - i32.add - local.tee 1 - i32.store offset=1048604 - i32.const 0 - local.get 5 - local.get 2 - i32.sub - local.tee 2 - i32.store offset=1048592 - local.get 1 - local.get 2 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - block ;; label = @3 - local.get 7 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@3;) - i32.const 0 - i32.load offset=1048588 - local.get 5 - i32.add - local.tee 5 - local.get 2 - i32.lt_u - br_if 1 (;@2;) - block ;; label = @4 - block ;; label = @5 - local.get 5 - local.get 2 - i32.sub - local.tee 1 - i32.const 16 - i32.lt_u - br_if 0 (;@5;) - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 2 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 6 - local.get 5 - i32.add - local.tee 5 - local.get 1 - i32.store - local.get 5 - local.get 5 - i32.load offset=4 - i32.const -2 - i32.and - i32.store offset=4 - br 1 (;@4;) - end - local.get 3 - local.get 4 - i32.const 1 - i32.and - local.get 5 - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 5 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - i32.const 0 - local.set 1 - i32.const 0 - local.set 2 - end - i32.const 0 - local.get 2 - i32.store offset=1048600 - i32.const 0 - local.get 1 - i32.store offset=1048588 - local.get 0 - return - end - local.get 7 - i32.load offset=4 - local.tee 8 - i32.const 2 - i32.and - br_if 0 (;@2;) - local.get 8 - i32.const -8 - i32.and - local.get 5 - i32.add - local.tee 9 - local.get 2 - i32.lt_u - br_if 0 (;@2;) - local.get 9 - local.get 2 - i32.sub - local.set 10 - block ;; label = @3 - block ;; label = @4 - local.get 8 - i32.const 255 - i32.gt_u - br_if 0 (;@4;) - local.get 7 - i32.load offset=8 - local.tee 1 - local.get 8 - i32.const 3 - i32.shr_u - local.tee 11 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 8 - i32.eq - drop - block ;; label = @5 - local.get 7 - i32.load offset=12 - local.tee 5 - local.get 1 - i32.ne - br_if 0 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 11 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@3;) - end - local.get 5 - local.get 8 - i32.eq - drop - local.get 5 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 5 - i32.store offset=12 - br 1 (;@3;) - end - local.get 7 - i32.load offset=24 - local.set 12 - block ;; label = @4 - block ;; label = @5 - local.get 7 - i32.load offset=12 - local.tee 8 - local.get 7 - i32.eq - br_if 0 (;@5;) - local.get 7 - i32.load offset=8 - local.tee 1 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 8 - local.get 1 - i32.store offset=8 - local.get 1 - local.get 8 - i32.store offset=12 - br 1 (;@4;) - end - block ;; label = @5 - local.get 7 - i32.const 20 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - local.get 7 - i32.const 16 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - i32.const 0 - local.set 8 - br 1 (;@4;) - end - loop ;; label = @5 - local.get 1 - local.set 11 - local.get 5 - local.tee 8 - i32.const 20 - i32.add - local.tee 1 - i32.load - local.tee 5 - br_if 0 (;@5;) - local.get 8 - i32.const 16 - i32.add - local.set 1 - local.get 8 - i32.load offset=16 - local.tee 5 - br_if 0 (;@5;) - end - local.get 11 - i32.const 0 - i32.store - end - local.get 12 - i32.eqz - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 7 - local.get 7 - i32.load offset=28 - local.tee 5 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 1 - i32.load - i32.ne - br_if 0 (;@5;) - local.get 1 - local.get 8 - i32.store - local.get 8 - br_if 1 (;@4;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@3;) - end - local.get 12 - i32.const 16 - i32.const 20 - local.get 12 - i32.load offset=16 - local.get 7 - i32.eq - select - i32.add - local.get 8 - i32.store - local.get 8 - i32.eqz - br_if 1 (;@3;) - end - local.get 8 - local.get 12 - i32.store offset=24 - block ;; label = @4 - local.get 7 - i32.load offset=16 - local.tee 1 - i32.eqz - br_if 0 (;@4;) - local.get 8 - local.get 1 - i32.store offset=16 - local.get 1 - local.get 8 - i32.store offset=24 - end - local.get 7 - i32.load offset=20 - local.tee 1 - i32.eqz - br_if 0 (;@3;) - local.get 8 - i32.const 20 - i32.add - local.get 1 - i32.store - local.get 1 - local.get 8 - i32.store offset=24 - end - block ;; label = @3 - local.get 10 - i32.const 15 - i32.gt_u - br_if 0 (;@3;) - local.get 3 - local.get 4 - i32.const 1 - i32.and - local.get 9 - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 9 - i32.add - local.tee 1 - local.get 1 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - return - end - local.get 3 - local.get 2 - local.get 4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 6 - local.get 2 - i32.add - local.tee 1 - local.get 10 - i32.const 3 - i32.or - i32.store offset=4 - local.get 6 - local.get 9 - i32.add - local.tee 2 - local.get 2 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 1 - local.get 10 - call $dispose_chunk - local.get 0 - return - end - block ;; label = @2 - local.get 1 - call $dlmalloc - local.tee 2 - br_if 0 (;@2;) - i32.const 0 - return - end - local.get 2 - local.get 0 - i32.const -4 - i32.const -8 - local.get 3 - i32.load - local.tee 5 - i32.const 3 - i32.and - select - local.get 5 - i32.const -8 - i32.and - i32.add - local.tee 5 - local.get 1 - local.get 5 - local.get 1 - i32.lt_u - select - call $memcpy - local.set 1 - local.get 0 - call $dlfree - local.get 1 - local.set 0 - end - local.get 0 - ) - (func $dispose_chunk (;9;) (type 4) (param i32 i32) - (local i32 i32 i32 i32 i32 i32) - local.get 0 - local.get 1 - i32.add - local.set 2 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 1 - i32.and - br_if 0 (;@2;) - local.get 3 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 0 - i32.load - local.tee 3 - local.get 1 - i32.add - local.set 1 - block ;; label = @3 - block ;; label = @4 - local.get 0 - local.get 3 - i32.sub - local.tee 0 - i32.const 0 - i32.load offset=1048600 - i32.eq - br_if 0 (;@4;) - block ;; label = @5 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 0 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - local.get 0 - i32.load offset=12 - local.tee 3 - local.get 4 - i32.ne - br_if 2 (;@3;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 3 (;@2;) - end - local.get 0 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 0 - i32.load offset=12 - local.tee 6 - local.get 0 - i32.eq - br_if 0 (;@6;) - local.get 0 - i32.load offset=8 - local.tee 3 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 0 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 0 - i32.const 16 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 3 - local.set 5 - local.get 4 - local.tee 6 - i32.const 20 - i32.add - local.tee 3 - i32.load - local.tee 4 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 3 - local.get 6 - i32.load offset=16 - local.tee 4 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 2 (;@2;) - block ;; label = @5 - block ;; label = @6 - local.get 0 - local.get 0 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 4 (;@2;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 0 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 3 (;@2;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 0 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 0 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 2 (;@2;) - local.get 6 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - br 2 (;@2;) - end - local.get 2 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.const 3 - i32.ne - br_if 1 (;@2;) - local.get 2 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - i32.const 0 - local.get 1 - i32.store offset=1048588 - local.get 2 - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - return - end - local.get 3 - local.get 6 - i32.eq - drop - local.get 3 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 3 - i32.store offset=12 - end - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.load offset=4 - local.tee 3 - i32.const 2 - i32.and - br_if 0 (;@3;) - block ;; label = @4 - local.get 2 - i32.const 0 - i32.load offset=1048604 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 0 - i32.store offset=1048604 - i32.const 0 - i32.const 0 - i32.load offset=1048592 - local.get 1 - i32.add - local.tee 1 - i32.store offset=1048592 - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 3 (;@1;) - i32.const 0 - i32.const 0 - i32.store offset=1048588 - i32.const 0 - i32.const 0 - i32.store offset=1048600 - return - end - block ;; label = @4 - local.get 2 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.get 0 - i32.store offset=1048600 - i32.const 0 - i32.const 0 - i32.load offset=1048588 - local.get 1 - i32.add - local.tee 1 - i32.store offset=1048588 - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - return - end - local.get 3 - i32.const -8 - i32.and - local.get 1 - i32.add - local.set 1 - block ;; label = @4 - block ;; label = @5 - local.get 3 - i32.const 255 - i32.gt_u - br_if 0 (;@5;) - local.get 2 - i32.load offset=8 - local.tee 4 - local.get 3 - i32.const 3 - i32.shr_u - local.tee 5 - i32.const 3 - i32.shl - i32.const 1048620 - i32.add - local.tee 6 - i32.eq - drop - block ;; label = @6 - local.get 2 - i32.load offset=12 - local.tee 3 - local.get 4 - i32.ne - br_if 0 (;@6;) - i32.const 0 - i32.const 0 - i32.load offset=1048580 - i32.const -2 - local.get 5 - i32.rotl - i32.and - i32.store offset=1048580 - br 2 (;@4;) - end - local.get 3 - local.get 6 - i32.eq - drop - local.get 3 - local.get 4 - i32.store offset=8 - local.get 4 - local.get 3 - i32.store offset=12 - br 1 (;@4;) - end - local.get 2 - i32.load offset=24 - local.set 7 - block ;; label = @5 - block ;; label = @6 - local.get 2 - i32.load offset=12 - local.tee 6 - local.get 2 - i32.eq - br_if 0 (;@6;) - local.get 2 - i32.load offset=8 - local.tee 3 - i32.const 0 - i32.load offset=1048596 - i32.lt_u - drop - local.get 6 - local.get 3 - i32.store offset=8 - local.get 3 - local.get 6 - i32.store offset=12 - br 1 (;@5;) - end - block ;; label = @6 - local.get 2 - i32.const 20 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - local.get 2 - i32.const 16 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - i32.const 0 - local.set 6 - br 1 (;@5;) - end - loop ;; label = @6 - local.get 4 - local.set 5 - local.get 3 - local.tee 6 - i32.const 20 - i32.add - local.tee 4 - i32.load - local.tee 3 - br_if 0 (;@6;) - local.get 6 - i32.const 16 - i32.add - local.set 4 - local.get 6 - i32.load offset=16 - local.tee 3 - br_if 0 (;@6;) - end - local.get 5 - i32.const 0 - i32.store - end - local.get 7 - i32.eqz - br_if 0 (;@4;) - block ;; label = @5 - block ;; label = @6 - local.get 2 - local.get 2 - i32.load offset=28 - local.tee 4 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.tee 3 - i32.load - i32.ne - br_if 0 (;@6;) - local.get 3 - local.get 6 - i32.store - local.get 6 - br_if 1 (;@5;) - i32.const 0 - i32.const 0 - i32.load offset=1048584 - i32.const -2 - local.get 4 - i32.rotl - i32.and - i32.store offset=1048584 - br 2 (;@4;) - end - local.get 7 - i32.const 16 - i32.const 20 - local.get 7 - i32.load offset=16 - local.get 2 - i32.eq - select - i32.add - local.get 6 - i32.store - local.get 6 - i32.eqz - br_if 1 (;@4;) - end - local.get 6 - local.get 7 - i32.store offset=24 - block ;; label = @5 - local.get 2 - i32.load offset=16 - local.tee 3 - i32.eqz - br_if 0 (;@5;) - local.get 6 - local.get 3 - i32.store offset=16 - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 2 - i32.load offset=20 - local.tee 3 - i32.eqz - br_if 0 (;@4;) - local.get 6 - i32.const 20 - i32.add - local.get 3 - i32.store - local.get 3 - local.get 6 - i32.store offset=24 - end - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - local.get 0 - i32.const 0 - i32.load offset=1048600 - i32.ne - br_if 1 (;@2;) - i32.const 0 - local.get 1 - i32.store offset=1048588 - return - end - local.get 2 - local.get 3 - i32.const -2 - i32.and - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.get 1 - i32.store - local.get 0 - local.get 1 - i32.const 1 - i32.or - i32.store offset=4 - end - block ;; label = @2 - local.get 1 - i32.const 255 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.const -8 - i32.and - i32.const 1048620 - i32.add - local.set 3 - block ;; label = @3 - block ;; label = @4 - i32.const 0 - i32.load offset=1048580 - local.tee 4 - i32.const 1 - local.get 1 - i32.const 3 - i32.shr_u - i32.shl - local.tee 1 - i32.and - br_if 0 (;@4;) - i32.const 0 - local.get 4 - local.get 1 - i32.or - i32.store offset=1048580 - local.get 3 - local.set 1 - br 1 (;@3;) - end - local.get 3 - i32.load offset=8 - local.set 1 - end - local.get 1 - local.get 0 - i32.store offset=12 - local.get 3 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 3 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - return - end - i32.const 31 - local.set 3 - block ;; label = @2 - local.get 1 - i32.const 16777215 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.const 38 - local.get 1 - i32.const 8 - i32.shr_u - i32.clz - local.tee 3 - i32.sub - i32.shr_u - i32.const 1 - i32.and - local.get 3 - i32.const 1 - i32.shl - i32.sub - i32.const 62 - i32.add - local.set 3 - end - local.get 0 - local.get 3 - i32.store offset=28 - local.get 0 - i64.const 0 - i64.store offset=16 align=4 - local.get 3 - i32.const 2 - i32.shl - i32.const 1048884 - i32.add - local.set 4 - block ;; label = @2 - i32.const 0 - i32.load offset=1048584 - local.tee 6 - i32.const 1 - local.get 3 - i32.shl - local.tee 2 - i32.and - br_if 0 (;@2;) - local.get 4 - local.get 0 - i32.store - i32.const 0 - local.get 6 - local.get 2 - i32.or - i32.store offset=1048584 - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=8 - local.get 0 - local.get 0 - i32.store offset=12 - return - end - local.get 1 - i32.const 0 - i32.const 25 - local.get 3 - i32.const 1 - i32.shr_u - i32.sub - local.get 3 - i32.const 31 - i32.eq - select - i32.shl - local.set 3 - local.get 4 - i32.load - local.set 6 - block ;; label = @2 - loop ;; label = @3 - local.get 6 - local.tee 4 - i32.load offset=4 - i32.const -8 - i32.and - local.get 1 - i32.eq - br_if 1 (;@2;) - local.get 3 - i32.const 29 - i32.shr_u - local.set 6 - local.get 3 - i32.const 1 - i32.shl - local.set 3 - local.get 4 - local.get 6 - i32.const 4 - i32.and - i32.add - i32.const 16 - i32.add - local.tee 2 - i32.load - local.tee 6 - br_if 0 (;@3;) - end - local.get 2 - local.get 0 - i32.store - local.get 0 - local.get 4 - i32.store offset=24 - local.get 0 - local.get 0 - i32.store offset=12 - local.get 0 - local.get 0 - i32.store offset=8 - return - end - local.get 4 - i32.load offset=8 - local.tee 1 - local.get 0 - i32.store offset=12 - local.get 4 - local.get 0 - i32.store offset=8 - local.get 0 - i32.const 0 - i32.store offset=24 - local.get 0 - local.get 4 - i32.store offset=12 - local.get 0 - local.get 1 - i32.store offset=8 - end - ) - (func $internal_memalign (;10;) (type 0) (param i32 i32) (result i32) - (local i32 i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.const 16 - local.get 0 - i32.const 16 - i32.gt_u - select - local.tee 2 - local.get 2 - i32.const -1 - i32.add - i32.and - br_if 0 (;@2;) - local.get 2 - local.set 0 - br 1 (;@1;) - end - i32.const 32 - local.set 3 - loop ;; label = @2 - local.get 3 - local.tee 0 - i32.const 1 - i32.shl - local.set 3 - local.get 0 - local.get 2 - i32.lt_u - br_if 0 (;@2;) - end - end - block ;; label = @1 - i32.const -64 - local.get 0 - i32.sub - local.get 1 - i32.gt_u - br_if 0 (;@1;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const 0 - return - end - block ;; label = @1 - local.get 0 - i32.const 16 - local.get 1 - i32.const 19 - i32.add - i32.const -16 - i32.and - local.get 1 - i32.const 11 - i32.lt_u - select - local.tee 1 - i32.add - i32.const 12 - i32.add - call $dlmalloc - local.tee 3 - br_if 0 (;@1;) - i32.const 0 - return - end - local.get 3 - i32.const -8 - i32.add - local.set 2 - block ;; label = @1 - block ;; label = @2 - local.get 0 - i32.const -1 - i32.add - local.get 3 - i32.and - br_if 0 (;@2;) - local.get 2 - local.set 0 - br 1 (;@1;) - end - local.get 3 - i32.const -4 - i32.add - local.tee 4 - i32.load - local.tee 5 - i32.const -8 - i32.and - local.get 3 - local.get 0 - i32.add - i32.const -1 - i32.add - i32.const 0 - local.get 0 - i32.sub - i32.and - i32.const -8 - i32.add - local.tee 3 - i32.const 0 - local.get 0 - local.get 3 - local.get 2 - i32.sub - i32.const 15 - i32.gt_u - select - i32.add - local.tee 0 - local.get 2 - i32.sub - local.tee 3 - i32.sub - local.set 6 - block ;; label = @2 - local.get 5 - i32.const 3 - i32.and - br_if 0 (;@2;) - local.get 0 - local.get 6 - i32.store offset=4 - local.get 0 - local.get 2 - i32.load - local.get 3 - i32.add - i32.store - br 1 (;@1;) - end - local.get 0 - local.get 6 - local.get 0 - i32.load offset=4 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 0 - local.get 6 - i32.add - local.tee 6 - local.get 6 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 4 - local.get 3 - local.get 4 - i32.load - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store - local.get 2 - local.get 3 - i32.add - local.tee 6 - local.get 6 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 2 - local.get 3 - call $dispose_chunk - end - block ;; label = @1 - local.get 0 - i32.load offset=4 - local.tee 3 - i32.const 3 - i32.and - i32.eqz - br_if 0 (;@1;) - local.get 3 - i32.const -8 - i32.and - local.tee 2 - local.get 1 - i32.const 16 - i32.add - i32.le_u - br_if 0 (;@1;) - local.get 0 - local.get 1 - local.get 3 - i32.const 1 - i32.and - i32.or - i32.const 2 - i32.or - i32.store offset=4 - local.get 0 - local.get 1 - i32.add - local.tee 3 - local.get 2 - local.get 1 - i32.sub - local.tee 1 - i32.const 3 - i32.or - i32.store offset=4 - local.get 0 - local.get 2 - i32.add - local.tee 2 - local.get 2 - i32.load offset=4 - i32.const 1 - i32.or - i32.store offset=4 - local.get 3 - local.get 1 - call $dispose_chunk - end - local.get 0 - i32.const 8 - i32.add - ) - (func $aligned_alloc (;11;) (type 0) (param i32 i32) (result i32) - block ;; label = @1 - local.get 0 - i32.const 16 - i32.gt_u - br_if 0 (;@1;) - local.get 1 - call $dlmalloc - return - end - local.get 0 - local.get 1 - call $internal_memalign - ) - (func $abort (;12;) (type 5) - unreachable - unreachable - ) - (func $sbrk (;13;) (type 2) (param i32) (result i32) - block ;; label = @1 - local.get 0 - br_if 0 (;@1;) - memory.size - i32.const 16 - i32.shl - return - end - block ;; label = @1 - local.get 0 - i32.const 65535 - i32.and - br_if 0 (;@1;) - local.get 0 - i32.const -1 - i32.le_s - br_if 0 (;@1;) - block ;; label = @2 - local.get 0 - i32.const 16 - i32.shr_u - memory.grow - local.tee 0 - i32.const -1 - i32.ne - br_if 0 (;@2;) - i32.const 0 - i32.const 48 - i32.store offset=1049076 - i32.const -1 - return - end - local.get 0 - i32.const 16 - i32.shl - return - end - call $abort - unreachable - ) - (func $memcpy (;14;) (type 6) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 2 - i32.const 32 - i32.gt_u - br_if 0 (;@3;) - local.get 1 - i32.const 3 - i32.and - i32.eqz - br_if 1 (;@2;) - local.get 2 - i32.eqz - br_if 1 (;@2;) - local.get 0 - local.get 1 - i32.load8_u - i32.store8 - local.get 2 - i32.const -1 - i32.add - local.set 3 - local.get 0 - i32.const 1 - i32.add - local.set 4 - local.get 1 - i32.const 1 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=1 - i32.store8 offset=1 - local.get 2 - i32.const -2 - i32.add - local.set 3 - local.get 0 - i32.const 2 - i32.add - local.set 4 - local.get 1 - i32.const 2 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=2 - i32.store8 offset=2 - local.get 2 - i32.const -3 - i32.add - local.set 3 - local.get 0 - i32.const 3 - i32.add - local.set 4 - local.get 1 - i32.const 3 - i32.add - local.tee 5 - i32.const 3 - i32.and - i32.eqz - br_if 2 (;@1;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - local.get 0 - local.get 1 - i32.load8_u offset=3 - i32.store8 offset=3 - local.get 2 - i32.const -4 - i32.add - local.set 3 - local.get 0 - i32.const 4 - i32.add - local.set 4 - local.get 1 - i32.const 4 - i32.add - local.set 5 - br 2 (;@1;) - end - local.get 0 - local.get 1 - local.get 2 - memory.copy - local.get 0 - return - end - local.get 2 - local.set 3 - local.get 0 - local.set 4 - local.get 1 - local.set 5 - end - block ;; label = @1 - block ;; label = @2 - local.get 4 - i32.const 3 - i32.and - local.tee 2 - br_if 0 (;@2;) - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.const 16 - i32.ge_u - br_if 0 (;@4;) - local.get 3 - local.set 2 - br 1 (;@3;) - end - block ;; label = @4 - local.get 3 - i32.const -16 - i32.add - local.tee 2 - i32.const 16 - i32.and - br_if 0 (;@4;) - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 4 - local.get 5 - i64.load offset=8 align=4 - i64.store offset=8 align=4 - local.get 4 - i32.const 16 - i32.add - local.set 4 - local.get 5 - i32.const 16 - i32.add - local.set 5 - local.get 2 - local.set 3 - end - local.get 2 - i32.const 16 - i32.lt_u - br_if 0 (;@3;) - local.get 3 - local.set 2 - loop ;; label = @4 - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 4 - local.get 5 - i64.load offset=8 align=4 - i64.store offset=8 align=4 - local.get 4 - local.get 5 - i64.load offset=16 align=4 - i64.store offset=16 align=4 - local.get 4 - local.get 5 - i64.load offset=24 align=4 - i64.store offset=24 align=4 - local.get 4 - i32.const 32 - i32.add - local.set 4 - local.get 5 - i32.const 32 - i32.add - local.set 5 - local.get 2 - i32.const -32 - i32.add - local.tee 2 - i32.const 15 - i32.gt_u - br_if 0 (;@4;) - end - end - block ;; label = @3 - local.get 2 - i32.const 8 - i32.lt_u - br_if 0 (;@3;) - local.get 4 - local.get 5 - i64.load align=4 - i64.store align=4 - local.get 5 - i32.const 8 - i32.add - local.set 5 - local.get 4 - i32.const 8 - i32.add - local.set 4 - end - block ;; label = @3 - local.get 2 - i32.const 4 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 4 - local.get 5 - i32.load - i32.store - local.get 5 - i32.const 4 - i32.add - local.set 5 - local.get 4 - i32.const 4 - i32.add - local.set 4 - end - block ;; label = @3 - local.get 2 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@3;) - local.get 4 - local.get 5 - i32.load16_u align=1 - i32.store16 align=1 - local.get 4 - i32.const 2 - i32.add - local.set 4 - local.get 5 - i32.const 2 - i32.add - local.set 5 - end - local.get 2 - i32.const 1 - i32.and - i32.eqz - br_if 1 (;@1;) - local.get 4 - local.get 5 - i32.load8_u - i32.store8 - local.get 0 - return - end - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - local.get 3 - i32.const 32 - i32.lt_u - br_if 0 (;@6;) - block ;; label = @7 - block ;; label = @8 - local.get 2 - i32.const -1 - i32.add - br_table 3 (;@5;) 0 (;@8;) 1 (;@7;) 7 (;@1;) - end - local.get 4 - local.get 5 - i32.load - i32.store16 align=1 - local.get 4 - local.get 5 - i32.const 2 - i32.add - i32.load align=2 - i32.store offset=2 - local.get 4 - local.get 5 - i32.const 6 - i32.add - i64.load align=2 - i64.store offset=6 align=4 - local.get 4 - i32.const 18 - i32.add - local.set 2 - local.get 5 - i32.const 18 - i32.add - local.set 1 - i32.const 14 - local.set 6 - local.get 5 - i32.const 14 - i32.add - i32.load align=2 - local.set 5 - i32.const 14 - local.set 3 - br 3 (;@4;) - end - local.get 4 - local.get 5 - i32.load - i32.store8 - local.get 4 - local.get 5 - i32.const 1 - i32.add - i32.load align=1 - i32.store offset=1 - local.get 4 - local.get 5 - i32.const 5 - i32.add - i64.load align=1 - i64.store offset=5 align=4 - local.get 4 - i32.const 17 - i32.add - local.set 2 - local.get 5 - i32.const 17 - i32.add - local.set 1 - i32.const 13 - local.set 6 - local.get 5 - i32.const 13 - i32.add - i32.load align=1 - local.set 5 - i32.const 15 - local.set 3 - br 2 (;@4;) - end - block ;; label = @6 - block ;; label = @7 - local.get 3 - i32.const 16 - i32.ge_u - br_if 0 (;@7;) - local.get 4 - local.set 2 - local.get 5 - local.set 1 - br 1 (;@6;) - end - local.get 4 - local.get 5 - i32.load8_u - i32.store8 - local.get 4 - local.get 5 - i32.load offset=1 align=1 - i32.store offset=1 align=1 - local.get 4 - local.get 5 - i64.load offset=5 align=1 - i64.store offset=5 align=1 - local.get 4 - local.get 5 - i32.load16_u offset=13 align=1 - i32.store16 offset=13 align=1 - local.get 4 - local.get 5 - i32.load8_u offset=15 - i32.store8 offset=15 - local.get 4 - i32.const 16 - i32.add - local.set 2 - local.get 5 - i32.const 16 - i32.add - local.set 1 - end - local.get 3 - i32.const 8 - i32.and - br_if 2 (;@3;) - br 3 (;@2;) - end - local.get 4 - local.get 5 - i32.load - local.tee 2 - i32.store8 - local.get 4 - local.get 2 - i32.const 16 - i32.shr_u - i32.store8 offset=2 - local.get 4 - local.get 2 - i32.const 8 - i32.shr_u - i32.store8 offset=1 - local.get 4 - local.get 5 - i32.const 3 - i32.add - i32.load align=1 - i32.store offset=3 - local.get 4 - local.get 5 - i32.const 7 - i32.add - i64.load align=1 - i64.store offset=7 align=4 - local.get 4 - i32.const 19 - i32.add - local.set 2 - local.get 5 - i32.const 19 - i32.add - local.set 1 - i32.const 15 - local.set 6 - local.get 5 - i32.const 15 - i32.add - i32.load align=1 - local.set 5 - i32.const 13 - local.set 3 - end - local.get 4 - local.get 6 - i32.add - local.get 5 - i32.store - end - local.get 2 - local.get 1 - i64.load align=1 - i64.store align=1 - local.get 2 - i32.const 8 - i32.add - local.set 2 - local.get 1 - i32.const 8 - i32.add - local.set 1 - end - block ;; label = @2 - local.get 3 - i32.const 4 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - local.get 1 - i32.load align=1 - i32.store align=1 - local.get 2 - i32.const 4 - i32.add - local.set 2 - local.get 1 - i32.const 4 - i32.add - local.set 1 - end - block ;; label = @2 - local.get 3 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@2;) - local.get 2 - local.get 1 - i32.load16_u align=1 - i32.store16 align=1 - local.get 2 - i32.const 2 - i32.add - local.set 2 - local.get 1 - i32.const 2 - i32.add - local.set 1 - end - local.get 3 - i32.const 1 - i32.and - i32.eqz - br_if 0 (;@1;) - local.get 2 - local.get 1 - i32.load8_u - i32.store8 - end - local.get 0 - ) - (func $cabi_realloc (;15;) (type 1) (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=1048576 - 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 - local.get 3 - call $__rust_realloc - local.set 2 - end - local.get 2 - br_if 0 (;@1;) - unreachable - unreachable - 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 "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;))) - (component (;0;) - (type (;0;) u64) - (export (;1;) "felt" (type 0)) - (type (;2;) (tuple 1 1 1 1)) - (export (;3;) "word" (type 2)) - (export (;4;) "account-id" (type 1)) - (export (;5;) "recipient" (type 3)) - (export (;6;) "tag" (type 1)) - (type (;7;) (record (field "asset" 4) (field "amount" u64))) - (export (;8;) "fungible-asset" (type 7)) - (export (;9;) "non-fungible-asset" (type 3)) - (type (;10;) (variant (case "fungible" 8) (case "non-fungible" 9))) - (export (;11;) "asset" (type 10)) - (type (;12;) (list 1)) - (export (;13;) "note-inputs" (type 12)) - ) - (instance (;0;) (instantiate 0)) - (export (;1;) "miden:base/types@1.0.0" (instance 0)) -) \ No newline at end of file diff --git a/tests/integration/expected/shl_i16.hir b/tests/integration/expected/shl_i16.hir deleted file mode 100644 index 8fe4b0353..000000000 --- a/tests/integration/expected/shl_i16.hir +++ /dev/null @@ -1,15 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 15 : i32; - v4 = band v1, v3 : i32; - v5 = shl.wrapping v0, v4 : i32; - ret v5; -} diff --git a/tests/integration/expected/shl_i16.masm b/tests/integration/expected/shl_i16.masm deleted file mode 100644 index 039722ffc..000000000 --- a/tests/integration/expected/shl_i16.masm +++ /dev/null @@ -1,672 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.15 - movup.2 - swap.1 - u32and - u32shl -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shl_i16.wat b/tests/integration/expected/shl_i16.wat deleted file mode 100644 index d8c7bae10..000000000 --- a/tests/integration/expected/shl_i16.wat +++ /dev/null @@ -1,19 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.const 15 - i32.and - i32.shl - i32.extend16_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shl_i32.hir b/tests/integration/expected/shl_i32.hir deleted file mode 100644 index cc2a16672..000000000 --- a/tests/integration/expected/shl_i32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = shl.wrapping v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/shl_i32.masm b/tests/integration/expected/shl_i32.masm deleted file mode 100644 index dd856d9ac..000000000 --- a/tests/integration/expected/shl_i32.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32shl -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shl_i32.wat b/tests/integration/expected/shl_i32.wat deleted file mode 100644 index 3d29aa416..000000000 --- a/tests/integration/expected/shl_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.shl - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shl_i8.hir b/tests/integration/expected/shl_i8.hir deleted file mode 100644 index cb012632e..000000000 --- a/tests/integration/expected/shl_i8.hir +++ /dev/null @@ -1,15 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 7 : i32; - v4 = band v1, v3 : i32; - v5 = shl.wrapping v0, v4 : i32; - ret v5; -} diff --git a/tests/integration/expected/shl_i8.masm b/tests/integration/expected/shl_i8.masm deleted file mode 100644 index 0ac88398a..000000000 --- a/tests/integration/expected/shl_i8.masm +++ /dev/null @@ -1,672 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.7 - movup.2 - swap.1 - u32and - u32shl -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shl_i8.wat b/tests/integration/expected/shl_i8.wat deleted file mode 100644 index 12ba9ed47..000000000 --- a/tests/integration/expected/shl_i8.wat +++ /dev/null @@ -1,19 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.const 7 - i32.and - i32.shl - i32.extend8_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shl_u16.hir b/tests/integration/expected/shl_u16.hir deleted file mode 100644 index 9de9f8cd5..000000000 --- a/tests/integration/expected/shl_u16.hir +++ /dev/null @@ -1,17 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 15 : i32; - v4 = band v1, v3 : i32; - v5 = shl.wrapping v0, v4 : i32; - v6 = const.i32 65535 : i32; - v7 = band v5, v6 : i32; - ret v7; -} diff --git a/tests/integration/expected/shl_u16.masm b/tests/integration/expected/shl_u16.masm deleted file mode 100644 index 912a024f2..000000000 --- a/tests/integration/expected/shl_u16.masm +++ /dev/null @@ -1,674 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.15 - movup.2 - swap.1 - u32and - u32shl - push.65535 - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shl_u16.wat b/tests/integration/expected/shl_u16.wat deleted file mode 100644 index 502f8d56a..000000000 --- a/tests/integration/expected/shl_u16.wat +++ /dev/null @@ -1,20 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.const 15 - i32.and - i32.shl - i32.const 65535 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shl_u32.hir b/tests/integration/expected/shl_u32.hir deleted file mode 100644 index cc2a16672..000000000 --- a/tests/integration/expected/shl_u32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = shl.wrapping v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/shl_u32.masm b/tests/integration/expected/shl_u32.masm deleted file mode 100644 index dd856d9ac..000000000 --- a/tests/integration/expected/shl_u32.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32shl -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shl_u32.wat b/tests/integration/expected/shl_u32.wat deleted file mode 100644 index 3d29aa416..000000000 --- a/tests/integration/expected/shl_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.shl - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shl_u64.hir b/tests/integration/expected/shl_u64.hir deleted file mode 100644 index c595fca7d..000000000 --- a/tests/integration/expected/shl_u64.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = shl.wrapping v0, v1 : i64; - ret v3; -} diff --git a/tests/integration/expected/shl_u64.wat b/tests/integration/expected/shl_u64.wat deleted file mode 100644 index e8c84c3b3..000000000 --- a/tests/integration/expected/shl_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 0 - local.get 1 - i64.shl - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shl_u8.hir b/tests/integration/expected/shl_u8.hir deleted file mode 100644 index 75fdaf17b..000000000 --- a/tests/integration/expected/shl_u8.hir +++ /dev/null @@ -1,17 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 7 : i32; - v4 = band v1, v3 : i32; - v5 = shl.wrapping v0, v4 : i32; - v6 = const.i32 255 : i32; - v7 = band v5, v6 : i32; - ret v7; -} diff --git a/tests/integration/expected/shl_u8.masm b/tests/integration/expected/shl_u8.masm deleted file mode 100644 index 715d172eb..000000000 --- a/tests/integration/expected/shl_u8.masm +++ /dev/null @@ -1,674 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.7 - movup.2 - swap.1 - u32and - u32shl - push.255 - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shl_u8.wat b/tests/integration/expected/shl_u8.wat deleted file mode 100644 index 6fc38ffa9..000000000 --- a/tests/integration/expected/shl_u8.wat +++ /dev/null @@ -1,20 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.const 7 - i32.and - i32.shl - i32.const 255 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shr_i16.hir b/tests/integration/expected/shr_i16.hir deleted file mode 100644 index c22c69301..000000000 --- a/tests/integration/expected/shr_i16.hir +++ /dev/null @@ -1,18 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 15 : i32; - v4 = band v1, v3 : i32; - v5 = shr.wrapping v0, v4 : i32; - br block1(v5); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/shr_i16.wat b/tests/integration/expected/shr_i16.wat deleted file mode 100644 index b1a57da0d..000000000 --- a/tests/integration/expected/shr_i16.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.const 15 - i32.and - i32.shr_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shr_i32.hir b/tests/integration/expected/shr_i32.hir deleted file mode 100644 index 71b5513e7..000000000 --- a/tests/integration/expected/shr_i32.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = shr.wrapping v0, v1 : i32; - br block1(v3); - -block1(v2: i32): - ret v2; -} diff --git a/tests/integration/expected/shr_i32.wat b/tests/integration/expected/shr_i32.wat deleted file mode 100644 index 230d81c1a..000000000 --- a/tests/integration/expected/shr_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.shr_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shr_i8.hir b/tests/integration/expected/shr_i8.hir deleted file mode 100644 index fa35d1284..000000000 --- a/tests/integration/expected/shr_i8.hir +++ /dev/null @@ -1,15 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 7 : i32; - v4 = band v1, v3 : i32; - v5 = shr.wrapping v0, v4 : i32; - ret v5; -} diff --git a/tests/integration/expected/shr_i8.masm b/tests/integration/expected/shr_i8.masm deleted file mode 100644 index bb615ee48..000000000 --- a/tests/integration/expected/shr_i8.masm +++ /dev/null @@ -1,672 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32d_div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32d_div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - push.7 - movup.2 - u32and - swap.1 - exec.checked_shr -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shr_i8.wat b/tests/integration/expected/shr_i8.wat deleted file mode 100644 index 02231f61e..000000000 --- a/tests/integration/expected/shr_i8.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.const 7 - i32.and - i32.shr_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shr_u16.hir b/tests/integration/expected/shr_u16.hir deleted file mode 100644 index 4c615bb4c..000000000 --- a/tests/integration/expected/shr_u16.hir +++ /dev/null @@ -1,18 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 15 : i32; - v4 = band v1, v3 : i32; - v5 = cast v0 : u32; - v6 = cast v4 : u32; - v7 = shr.wrapping v5, v6 : u32; - v8 = cast v7 : i32; - ret v8; -} diff --git a/tests/integration/expected/shr_u16.masm b/tests/integration/expected/shr_u16.masm deleted file mode 100644 index 1986942f3..000000000 --- a/tests/integration/expected/shr_u16.masm +++ /dev/null @@ -1,687 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - push.15 - movup.2 - swap.1 - u32and - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32shr - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shr_u16.wat b/tests/integration/expected/shr_u16.wat deleted file mode 100644 index d2696cd0c..000000000 --- a/tests/integration/expected/shr_u16.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.const 15 - i32.and - i32.shr_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shr_u32.hir b/tests/integration/expected/shr_u32.hir deleted file mode 100644 index b060de6d8..000000000 --- a/tests/integration/expected/shr_u32.hir +++ /dev/null @@ -1,16 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = cast v0 : u32; - v4 = cast v1 : u32; - v5 = shr.wrapping v3, v4 : u32; - v6 = cast v5 : i32; - ret v6; -} diff --git a/tests/integration/expected/shr_u32.masm b/tests/integration/expected/shr_u32.masm deleted file mode 100644 index f3948d7e2..000000000 --- a/tests/integration/expected/shr_u32.masm +++ /dev/null @@ -1,684 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - swap.1 - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32shr - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shr_u32.wat b/tests/integration/expected/shr_u32.wat deleted file mode 100644 index bb442959e..000000000 --- a/tests/integration/expected/shr_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.shr_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/shr_u8.hir b/tests/integration/expected/shr_u8.hir deleted file mode 100644 index e799b232e..000000000 --- a/tests/integration/expected/shr_u8.hir +++ /dev/null @@ -1,18 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = const.i32 7 : i32; - v4 = band v1, v3 : i32; - v5 = cast v0 : u32; - v6 = cast v4 : u32; - v7 = shr.wrapping v5, v6 : u32; - v8 = cast v7 : i32; - ret v8; -} diff --git a/tests/integration/expected/shr_u8.masm b/tests/integration/expected/shr_u8.masm deleted file mode 100644 index 1ca6e8b39..000000000 --- a/tests/integration/expected/shr_u8.masm +++ /dev/null @@ -1,687 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - push.7 - movup.2 - swap.1 - u32and - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz - u32shr - dup.0 - push.2147483648 - u32and - eq.2147483648 - assertz -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/shr_u8.wat b/tests/integration/expected/shr_u8.wat deleted file mode 100644 index 94942a283..000000000 --- a/tests/integration/expected/shr_u8.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.const 7 - i32.and - i32.shr_u - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/sub_i16.hir b/tests/integration/expected/sub_i16.hir deleted file mode 100644 index 67eb7d3f9..000000000 --- a/tests/integration/expected/sub_i16.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = sub.wrapping v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/sub_i16.masm b/tests/integration/expected/sub_i16.masm deleted file mode 100644 index bcdcaff6a..000000000 --- a/tests/integration/expected/sub_i16.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32wrapping_sub -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/sub_i16.wat b/tests/integration/expected/sub_i16.wat deleted file mode 100644 index 9d7ac9cd2..000000000 --- a/tests/integration/expected/sub_i16.wat +++ /dev/null @@ -1,17 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.sub - i32.extend16_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/sub_i32.hir b/tests/integration/expected/sub_i32.hir deleted file mode 100644 index 67eb7d3f9..000000000 --- a/tests/integration/expected/sub_i32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = sub.wrapping v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/sub_i32.masm b/tests/integration/expected/sub_i32.masm deleted file mode 100644 index bcdcaff6a..000000000 --- a/tests/integration/expected/sub_i32.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32wrapping_sub -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/sub_i32.wat b/tests/integration/expected/sub_i32.wat deleted file mode 100644 index 31be40fcc..000000000 --- a/tests/integration/expected/sub_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.sub - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/sub_i8.hir b/tests/integration/expected/sub_i8.hir deleted file mode 100644 index 67eb7d3f9..000000000 --- a/tests/integration/expected/sub_i8.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = sub.wrapping v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/sub_i8.masm b/tests/integration/expected/sub_i8.masm deleted file mode 100644 index bcdcaff6a..000000000 --- a/tests/integration/expected/sub_i8.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32wrapping_sub -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/sub_i8.wat b/tests/integration/expected/sub_i8.wat deleted file mode 100644 index 64be7d049..000000000 --- a/tests/integration/expected/sub_i8.wat +++ /dev/null @@ -1,17 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.sub - i32.extend8_s - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/sub_u16.hir b/tests/integration/expected/sub_u16.hir deleted file mode 100644 index d0e7d310e..000000000 --- a/tests/integration/expected/sub_u16.hir +++ /dev/null @@ -1,15 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = sub.wrapping v0, v1 : i32; - v4 = const.i32 65535 : i32; - v5 = band v3, v4 : i32; - ret v5; -} diff --git a/tests/integration/expected/sub_u16.masm b/tests/integration/expected/sub_u16.masm deleted file mode 100644 index 4b544a53e..000000000 --- a/tests/integration/expected/sub_u16.masm +++ /dev/null @@ -1,671 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32wrapping_sub - push.65535 - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/sub_u16.wat b/tests/integration/expected/sub_u16.wat deleted file mode 100644 index ec3cd27f3..000000000 --- a/tests/integration/expected/sub_u16.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.sub - i32.const 65535 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/sub_u32.hir b/tests/integration/expected/sub_u32.hir deleted file mode 100644 index 67eb7d3f9..000000000 --- a/tests/integration/expected/sub_u32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = sub.wrapping v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/sub_u32.masm b/tests/integration/expected/sub_u32.masm deleted file mode 100644 index bcdcaff6a..000000000 --- a/tests/integration/expected/sub_u32.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32wrapping_sub -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/sub_u32.wat b/tests/integration/expected/sub_u32.wat deleted file mode 100644 index 31be40fcc..000000000 --- a/tests/integration/expected/sub_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.sub - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/sub_u64.hir b/tests/integration/expected/sub_u64.hir deleted file mode 100644 index cca2e41ed..000000000 --- a/tests/integration/expected/sub_u64.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i64, i64) -> i64 { -block0(v0: i64, v1: i64): - v3 = sub.wrapping v0, v1 : i64; - ret v3; -} diff --git a/tests/integration/expected/sub_u64.wat b/tests/integration/expected/sub_u64.wat deleted file mode 100644 index efd24694e..000000000 --- a/tests/integration/expected/sub_u64.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i64 i64) (result i64))) - (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) - local.get 0 - local.get 1 - i64.sub - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/sub_u8.hir b/tests/integration/expected/sub_u8.hir deleted file mode 100644 index f1bb1c449..000000000 --- a/tests/integration/expected/sub_u8.hir +++ /dev/null @@ -1,15 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = sub.wrapping v0, v1 : i32; - v4 = const.i32 255 : i32; - v5 = band v3, v4 : i32; - ret v5; -} diff --git a/tests/integration/expected/sub_u8.masm b/tests/integration/expected/sub_u8.masm deleted file mode 100644 index c3a528d85..000000000 --- a/tests/integration/expected/sub_u8.masm +++ /dev/null @@ -1,671 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32wrapping_sub - push.255 - u32and -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/sub_u8.wat b/tests/integration/expected/sub_u8.wat deleted file mode 100644 index 533800ec5..000000000 --- a/tests/integration/expected/sub_u8.wat +++ /dev/null @@ -1,18 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.sub - i32.const 255 - i32.and - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/xor_bool.hir b/tests/integration/expected/xor_bool.hir deleted file mode 100644 index 7c8363ada..000000000 --- a/tests/integration/expected/xor_bool.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bxor v0, v1 : i32; - ret v3; -} diff --git a/tests/integration/expected/xor_bool.masm b/tests/integration/expected/xor_bool.masm deleted file mode 100644 index 5b3d6fddc..000000000 --- a/tests/integration/expected/xor_bool.masm +++ /dev/null @@ -1,669 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - swap.1 - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/xor_bool.wat b/tests/integration/expected/xor_bool.wat deleted file mode 100644 index fdf46ac19..000000000 --- a/tests/integration/expected/xor_bool.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 0 - local.get 1 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/xor_i16.hir b/tests/integration/expected/xor_i16.hir deleted file mode 100644 index 63b788944..000000000 --- a/tests/integration/expected/xor_i16.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bxor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/xor_i16.masm b/tests/integration/expected/xor_i16.masm deleted file mode 100644 index fc4c6360e..000000000 --- a/tests/integration/expected/xor_i16.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/xor_i16.wat b/tests/integration/expected/xor_i16.wat deleted file mode 100644 index 3b4228faa..000000000 --- a/tests/integration/expected/xor_i16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/xor_i32.hir b/tests/integration/expected/xor_i32.hir deleted file mode 100644 index 63b788944..000000000 --- a/tests/integration/expected/xor_i32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bxor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/xor_i32.masm b/tests/integration/expected/xor_i32.masm deleted file mode 100644 index fc4c6360e..000000000 --- a/tests/integration/expected/xor_i32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/xor_i32.wat b/tests/integration/expected/xor_i32.wat deleted file mode 100644 index 3b4228faa..000000000 --- a/tests/integration/expected/xor_i32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/xor_i8.hir b/tests/integration/expected/xor_i8.hir deleted file mode 100644 index 63b788944..000000000 --- a/tests/integration/expected/xor_i8.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bxor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/xor_i8.masm b/tests/integration/expected/xor_i8.masm deleted file mode 100644 index fc4c6360e..000000000 --- a/tests/integration/expected/xor_i8.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/xor_i8.wat b/tests/integration/expected/xor_i8.wat deleted file mode 100644 index 3b4228faa..000000000 --- a/tests/integration/expected/xor_i8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/xor_u16.hir b/tests/integration/expected/xor_u16.hir deleted file mode 100644 index 63b788944..000000000 --- a/tests/integration/expected/xor_u16.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bxor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/xor_u16.masm b/tests/integration/expected/xor_u16.masm deleted file mode 100644 index fc4c6360e..000000000 --- a/tests/integration/expected/xor_u16.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/xor_u16.wat b/tests/integration/expected/xor_u16.wat deleted file mode 100644 index 3b4228faa..000000000 --- a/tests/integration/expected/xor_u16.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/xor_u32.hir b/tests/integration/expected/xor_u32.hir deleted file mode 100644 index 63b788944..000000000 --- a/tests/integration/expected/xor_u32.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bxor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/xor_u32.masm b/tests/integration/expected/xor_u32.masm deleted file mode 100644 index fc4c6360e..000000000 --- a/tests/integration/expected/xor_u32.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/xor_u32.wat b/tests/integration/expected/xor_u32.wat deleted file mode 100644 index 3b4228faa..000000000 --- a/tests/integration/expected/xor_u32.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/expected/xor_u8.hir b/tests/integration/expected/xor_u8.hir deleted file mode 100644 index 63b788944..000000000 --- a/tests/integration/expected/xor_u8.hir +++ /dev/null @@ -1,13 +0,0 @@ -module noname - -const $0 = 0x00100000; - -global external @__stack_pointer : i32 = $0 { id = 0 }; -global external @gv1 : i32 = $0 { id = 1 }; -global external @gv2 : i32 = $0 { id = 2 }; - -pub fn entrypoint(i32, i32) -> i32 { -block0(v0: i32, v1: i32): - v3 = bxor v1, v0 : i32; - ret v3; -} diff --git a/tests/integration/expected/xor_u8.masm b/tests/integration/expected/xor_u8.masm deleted file mode 100644 index fc4c6360e..000000000 --- a/tests/integration/expected/xor_u8.masm +++ /dev/null @@ -1,668 +0,0 @@ -mod intrinsics::i32 - -export.is_signed - push.2147483648 - u32and - push.2147483648 - eq -end - -export.unchecked_neg - u32not - u32wrapping_add.1 -end - -export.checked_neg - dup.0 - push.2147483648 - eq - assertz - exec.unchecked_neg -end - -export.overflowing_add - u32assert2 - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - eq - movup.3 - movup.3 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and -end - -export.wrapping_add - exec.overflowing_add - drop -end - -export.checked_add - exec.overflowing_add - assertz -end - -export.overflowing_sub - u32assert2 - dup.0 - push.2147483648 - eq - if.true - drop - push.2147483647 - dup.1 - exec.is_signed - dup.0 - eq.0 - movup.3 - movup.3 - u32wrapping_add - push.1 - u32wrapping_add - dup.0 - exec.is_signed - movup.3 - neq - movup.2 - and - else - exec.unchecked_neg - exec.overflowing_add - end -end - -export.wrapping_sub - exec.overflowing_sub - drop -end - -export.checked_sub - exec.overflowing_sub - assertz -end - -export.overflowing_mul - u32assert2 - dup.0 - push.2147483648 - eq - dup.2 - push.2147483648 - eq - or - if.true - dup.0 - eq.1 - dup.2 - eq.1 - or - movup.2 - push.4294967295 - eq - movup.2 - push.4294967295 - eq - or - dup.1 - or - push.2147483648 - push.0 - swap.2 - cdrop - swap.1 - not - else - dup.0 - exec.is_signed - dup.2 - exec.is_signed - dup.1 - dup.1 - neq - movdn.4 - movup.3 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - swap.2 - dup.0 - exec.unchecked_neg - movup.2 - cdrop - u32overflowing_mul - dup.1 - exec.is_signed - or - swap.1 - dup.0 - exec.unchecked_neg - movup.3 - cdrop - swap.1 - end -end - -export.wrapping_mul - exec.overflowing_mul - drop -end - -export.checked_mul - exec.overflowing_mul - assertz -end - -export.checked_div - u32assert2 - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.4 - cdrop - dup.1 - exec.unchecked_neg - dup.2 - swap.1 - movup.3 - exec.is_signed - dup.0 - movdn.5 - cdrop - u32div - movdn.2 - neq - dup.1 - exec.unchecked_neg - swap.1 - cdrop -end - -export.icmp - dup.1 - dup.1 - push.2147483648 - u32and - swap.1 - push.2147483648 - u32and - eq.0 - swap.1 - eq.0 - swap.1 - dup.1 - neq - if.true - movdn.2 - drop - drop - push.4294967295 - push.1 - swap.2 - cdrop - else - drop - dup.1 - dup.1 - u32gt - movdn.2 - u32lt - push.0 - push.4294967295 - push.1 - swap.3 - cdrop - swap.2 - cdrop - end -end - -export.is_lt - exec.icmp - push.4294967295 - eq -end - -export.is_lte - exec.icmp - neq.1 -end - -export.is_gt - exec.icmp - eq.1 -end - -export.is_gte - exec.icmp - push.4294967295 - neq -end - -export.pow2 - dup.0 - push.31 - u32lt - assert - push.1 - swap.1 - u32shl -end - -export.ipow - dup.0 - push.31 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - push.1 - push.0 - swap.2 - cdrop - swap.1 - drop - else - push.1 - dup.1 - push.1 - u32gt - while.true - dup.2 - dup.1 - u32wrapping_mul - dup.2 - push.1 - u32and - eq.1 - cdrop - swap.1 - u32div.2 - movup.2 - dup.0 - u32wrapping_mul - swap.1 - movup.2 - dup.1 - push.1 - u32gt - end - swap.1 - drop - u32wrapping_mul - end -end - -export.checked_shr - dup.0 - push.32 - u32lt - assert - dup.0 - eq.0 - dup.2 - eq.0 - or - if.true - eq.0 - swap.1 - push.0 - swap.2 - cdrop - else - dup.1 - push.2147483648 - u32and - push.2147483648 - eq - if.true - swap.1 - dup.1 - u32shr - push.1 - dup.2 - u32shl - sub.1 - push.32 - movup.3 - sub - u32shl - u32or - u32assert - else - u32shr - u32assert - end - end -end - -mod intrinsics::mem - -export.extract_element - dup.0 - push.3 - lte - assert - dup.0 - push.3 - lt - movdn.5 - dup.0 - push.2 - lt - movdn.5 - push.1 - lt - cdrop - movup.3 - cdrop - movup.2 - cdrop -end - -proc.load_felt_unchecked - padw - movup.4 - mem_loadw - movup.4 - exec.extract_element -end - -export.load_felt - movup.2 - assertz - exec.load_felt_unchecked -end - -export.load_sw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - exec.load_felt_unchecked - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.3 - movup.3 - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movdn.2 - movdn.2 - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - dup.2 - u32shl - swap.1 - push.32 - movup.3 - u32overflowing_sub - assertz - u32shr - u32or - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movdn.4 - drop - drop - drop - push.32 - dup.3 - u32overflowing_sub - assertz - u32shr - swap.1 - padw - movup.4 - mem_loadw - drop - drop - drop - movup.2 - u32shl - u32or - end - end - end - end -end - -export.realign_dw - dup.3 - u32shl - movdn.2 - dup.0 - push.32 - dup.4 - u32shr - movup.4 - u32or - movdn.2 - dup.3 - u32shl - swap.1 - push.32 - movup.4 - u32shr - u32or - swap.1 -end - -export.load_dw - dup.2 - eq.0 - dup.3 - push.8 - u32lt - assert - if.true - movup.2 - drop - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - movup.3 - drop - else - swap.1 - eq.2 - if.true - padw - movup.4 - mem_loadw - drop - drop - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - movup.4 - padw - movup.4 - mem_loadw - drop - drop - drop - end - end - end - else - dup.1 - eq.0 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - movup.4 - drop - exec.realign_dw - else - dup.1 - eq.1 - if.true - swap.1 - drop - padw - movup.4 - mem_loadw - drop - exec.realign_dw - else - swap.1 - eq.2 - if.true - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - movup.4 - drop - drop - drop - swap.1 - padw - movup.4 - mem_loadw - drop - drop - exec.realign_dw - else - dup.0 - u32overflowing_add.1 - assertz - padw - movup.4 - mem_loadw - movup.4 - movup.4 - drop - drop - movup.2 - padw - movup.4 - mem_loadw - drop - drop - drop - exec.realign_dw - end - end - end - end -end - -mod noname - -export.entrypoint - u32xor -end - -program - -use noname - -begin - exec.noname::entrypoint -end diff --git a/tests/integration/expected/xor_u8.wat b/tests/integration/expected/xor_u8.wat deleted file mode 100644 index 3b4228faa..000000000 --- a/tests/integration/expected/xor_u8.wat +++ /dev/null @@ -1,16 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) - local.get 1 - local.get 0 - i32.xor - ) - (memory (;0;) 16) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (global (;1;) i32 i32.const 1048576) - (global (;2;) i32 i32.const 1048576) - (export "memory" (memory 0)) - (export "entrypoint" (func $entrypoint)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) -) \ No newline at end of file diff --git a/tests/integration/src/compiler_test.rs b/tests/integration/src/compiler_test.rs deleted file mode 100644 index d518c3f04..000000000 --- a/tests/integration/src/compiler_test.rs +++ /dev/null @@ -1,512 +0,0 @@ -#![allow(dead_code)] - -use std::fs; -use std::io::Read; -use std::path::Path; -use std::process::Command; -use std::process::Stdio; -use std::sync::Arc; - -use miden_assembly::Assembler; -use miden_assembly::AssemblyContext; -use miden_codegen_masm::MasmCompiler; -use miden_diagnostics::term::termcolor::ColorChoice; -use miden_diagnostics::CodeMap; -use miden_diagnostics::DefaultEmitter; -use miden_diagnostics::DiagnosticsConfig; -use miden_diagnostics::DiagnosticsHandler; -use miden_diagnostics::Emitter; -use miden_diagnostics::NullEmitter; -use miden_diagnostics::SourceSpan; -use miden_diagnostics::Verbosity; -use miden_frontend_wasm::translate_module; -use miden_frontend_wasm::WasmTranslationConfig; - -use miden_hir::pass::AnalysisManager; -use miden_hir::pass::RewritePass; -use miden_hir::pass::RewriteSet; -use miden_hir::FunctionIdent; -use miden_hir::Ident; -use miden_hir::ModuleRewritePassAdapter; -use miden_hir::ProgramBuilder; -use miden_hir::Symbol; -use miden_stdlib::StdLibrary; -use midenc_session::InputFile; -use midenc_session::Session; - -pub enum CompilerTestSource { - Rust(String), - RustCargo { - cargo_project_folder_name: String, - artifact_name: String, - }, - // Wasm(String), - // Ir(String), -} - -impl CompilerTestSource { - pub fn artifact_name(&self) -> String { - match self { - CompilerTestSource::RustCargo { - cargo_project_folder_name: _, - artifact_name, - } => artifact_name.clone(), - _ => panic!("Not a Rust Cargo project"), - } - } -} - -/// Compile to different stages (e.g. Wasm, IR, MASM) and compare the results against expected output -pub struct CompilerTest { - /// The compiler session - pub session: Session, - /// The source code used to compile the test - pub source: CompilerTestSource, - /// The entrypoint function to use when building the IR - entrypoint: Option, - /// The compiled Wasm component/module - pub wasm_bytes: Vec, - /// The WIT Rust bindings generated by the wit-bindgen (if Wasm component is compiled) - pub wit_bind: Option, - /// The compiled IR - pub hir: Option>, - /// The compiled MASM - pub ir_masm: Option>, -} - -impl CompilerTest { - /// Compile the Wasm component from a Rust Cargo project using cargo-component - pub fn rust_source_cargo_component(cargo_project_folder: &str) -> Self { - let manifest_path = format!("../rust-apps-wasm/{}/Cargo.toml", cargo_project_folder); - // dbg!(&pwd); - let mut cargo_build_cmd = Command::new("cargo"); - 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() - .expect( - format!( - "Failed to execute cargo build {}.", - cargo_build_cmd - .get_args() - .map(|arg| format!("'{}'", arg.to_str().unwrap())) - .collect::>() - .join(" ") - ) - .as_str(), - ); - let reader = std::io::BufReader::new(child.stdout.take().unwrap()); - let mut wasm_artifacts = Vec::new(); - for message in cargo_metadata::Message::parse_stream(reader) { - match message.expect("Failed to parse cargo metadata") { - cargo_metadata::Message::CompilerArtifact(artifact) => { - // find the Wasm artifact in artifact.filenames - for filename in artifact.filenames { - if filename.as_str().ends_with(".wasm") { - wasm_artifacts.push(filename.into_std_path_buf()); - } - } - } - _ => (), - } - } - let output = child.wait().expect("Couldn't get cargo's exit status"); - if !output.success() { - eprintln!("pwd: {:?}", std::env::current_dir().unwrap()); - let mut stderr = Vec::new(); - child - .stderr - .unwrap() - .read(&mut stderr) - .expect("Failed to read stderr"); - let stderr = String::from_utf8(stderr).expect("Failed to parse stderr"); - eprintln!("stderr: {}", stderr); - panic!("Rust to Wasm compilation failed!"); - } - 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 wasm_comp_filename = wasm_comp_path - .file_stem() - .unwrap() - .to_str() - .unwrap() - .to_string(); - let target_dir = &wasm_comp_path - .parent() // remove file - .unwrap() - .parent() // remove release - .unwrap() - .parent() // remove wasm32-wasi (target) - .unwrap(); - let crate_name_dashed = wasm_comp_filename.replace("_", "-"); - let wit_bind_path = target_dir - .join("bindings") - .join(crate_name_dashed) - .join("bindings.rs"); - dbg!(&wit_bind_path); - let wit_bind_str = String::from_utf8(std::fs::read(wit_bind_path).unwrap()).unwrap(); - Self { - session: default_session(), - source: CompilerTestSource::RustCargo { - cargo_project_folder_name: cargo_project_folder.to_string(), - artifact_name, - }, - entrypoint: None, - wasm_bytes: fs::read(wasm_artifacts.first().unwrap()).unwrap(), - wit_bind: Some(wit_bind_str), - hir: None, - ir_masm: None, - } - } - - /// 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, - ) -> 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"); - // dbg!(&target_bin_file_path); - let mut target_bin_file = fs::File::open(target_bin_file_path).unwrap(); - let mut wasm_bytes = vec![]; - Read::read_to_end(&mut target_bin_file, &mut wasm_bytes).unwrap(); - fs::remove_dir_all(target_dir).unwrap(); - - let session = default_session(); - let entrypoint = FunctionIdent { - module: Ident::new(Symbol::intern("noname"), 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(), - }, - wasm_bytes, - entrypoint: Some(entrypoint), - hir: None, - ir_masm: None, - wit_bind: None, - } - } - - /// Set the Rust source code to compile - pub fn rust_source_program(rust_source: &str) -> Self { - let wasm_bytes = compile_rust_file(rust_source); - let session = default_session(); - CompilerTest { - session, - source: CompilerTestSource::Rust(rust_source.to_string()), - wasm_bytes, - entrypoint: None, - hir: None, - ir_masm: None, - wit_bind: None, - } - } - - /// Set the Rust source code to compile and add a binary operation test - pub fn rust_fn_body(rust_source: &str) -> Self { - let rust_source = format!( - r#" - #![no_std] - #![no_main] - - #[panic_handler] - fn my_panic(_info: &core::panic::PanicInfo) -> ! {{ - loop {{}} - }} - - #[no_mangle] - pub extern "C" fn entrypoint{} - "#, - rust_source - ); - let wasm_bytes = compile_rust_file(&rust_source); - let session = default_session(); - let entrypoint = FunctionIdent { - module: Ident { - name: Symbol::intern("noname"), - span: SourceSpan::default(), - }, - function: Ident { - name: Symbol::intern("entrypoint"), - span: SourceSpan::default(), - }, - }; - - CompilerTest { - session, - source: CompilerTestSource::Rust(rust_source.to_string()), - wasm_bytes, - entrypoint: Some(entrypoint), - hir: None, - ir_masm: None, - wit_bind: None, - } - } - - /// Compare the compiled Wasm against the expected output - pub fn expect_wasm(&self, expected_wat_file: expect_test::ExpectFile) { - let wasm_bytes = self.wasm_bytes.as_ref(); - let wat = demangle(&wasm_to_wat(wasm_bytes)); - expected_wat_file.assert_eq(&wat); - } - - /// Compare the generated WIT Rust bindings against the expected output - pub fn expect_wit_bind(&self, expected_wit_bind_file: expect_test::ExpectFile) { - let wit_bind = self.wit_bind.as_ref().unwrap(); - expected_wit_bind_file.assert_eq(&wit_bind); - } - - /// Compare the compiled IR against the expected output - pub fn expect_ir(&mut self, expected_hir_file: expect_test::ExpectFile) { - let hir_program = if let Some(hir) = self.hir.as_ref() { - hir - } else { - let hir_module = wasm_to_ir(&self.wasm_bytes, &self.session); - let mut builder = ProgramBuilder::new(&self.session.diagnostics) - .with_module(hir_module.into()) - .unwrap(); - if let Some(entrypoint) = self.entrypoint.as_ref() { - builder = builder.with_entrypoint(entrypoint.clone()); - } - let hir_program = builder.link().expect("Failed to link IR program"); - self.hir = Some(hir_program); - self.hir.as_ref().unwrap() - }; - // 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(), - ); - expected_hir_file.assert_eq(&ir_module); - } - - /// Compare the compiled MASM against the expected output - pub fn expect_masm(&mut self, expected_masm_file: expect_test::ExpectFile) { - let program = self.ir_masm_program(); - expected_masm_file.assert_eq(&program.to_string()); - } - - /// Get the compiled MASM as [`miden_assembly::Program`] - pub fn vm_masm_program(&mut self) -> miden_core::Program { - let assembler = Assembler::default() - .with_library(&StdLibrary::default()) - .expect("Failed to load stdlib"); - let program = self.ir_masm_program(); - // TODO: get code map from the self.diagnostics - let codemap = CodeMap::new(); - let program_ast = program.to_program_ast(&codemap); - for module in program.modules() { - let core_module = module.to_module_ast(&codemap); - let _ = assembler - .compile_module( - &core_module.ast, - Some(&core_module.path), - &mut AssemblyContext::for_module(false), - ) - .expect( - format!( - "VM Assembler failed to compile module:\n{:?}\n with error", - core_module.ast - ) - .as_str(), - ); - } - let core_program = assembler.compile_ast(&program_ast).unwrap(); - core_program - } - - /// Get the compiled MASM as [`miden_codegen_masm::Program`] - pub fn ir_masm_program(&mut self) -> Arc { - if self.ir_masm.is_none() { - let mut compiler = MasmCompiler::new(&self.session); - let hir = self.hir.take().expect("IR is not compiled"); - let ir_masm = compiler.compile(hir).unwrap(); - let frozen = ir_masm.freeze(); - self.ir_masm = Some(frozen); - } - self.ir_masm.clone().unwrap() - } -} - -pub(crate) fn demangle(name: &str) -> String { - let mut input = name.as_bytes(); - let mut demangled = Vec::new(); - let include_hash = false; - rustc_demangle::demangle_stream(&mut input, &mut demangled, include_hash).unwrap(); - String::from_utf8(demangled).unwrap() -} - -pub(crate) fn wasm_to_wat(wasm_bytes: &[u8]) -> String { - let mut wasm_printer = wasmprinter::Printer::new(); - // disable printing of the "producers" section because it contains a rustc version - // to not brake tests when rustc is updated - wasm_printer.add_custom_section_printer("producers", |_, _, _| Ok(())); - let wat = wasm_printer.print(wasm_bytes.as_ref()).unwrap(); - wat -} -fn compile_rust_file(rust_source: &str) -> Vec { - let rustc_opts = [ - "-C", - "opt-level=z", // optimize for size - "--target", - "wasm32-unknown-unknown", - ]; - let file_name = 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!"); - } - let wasm = fs::read(&output_file).unwrap(); - fs::remove_dir_all(proj_dir).unwrap(); - return wasm; -} - -fn default_emitter(verbosity: Verbosity, color: ColorChoice) -> Arc { - match verbosity { - Verbosity::Silent => Arc::new(NullEmitter::new(color)), - _ => Arc::new(DefaultEmitter::new(color)), - } -} - -fn make_diagnostics() -> DiagnosticsHandler { - let codemap = Arc::new(CodeMap::new()); - let diagnostics = DiagnosticsHandler::new( - DiagnosticsConfig { - verbosity: Verbosity::Debug, - warnings_as_errors: false, - no_warn: false, - display: Default::default(), - }, - codemap, - default_emitter(Verbosity::Debug, ColorChoice::Auto), - ); - diagnostics -} - -/// Create a default session for testing -pub fn default_session() -> Session { - let session = Session::new( - Default::default(), - InputFile::from_path("test.hir").unwrap(), - None, - None, - None, - Default::default(), - None, - ); - session -} - -fn hash_string(inputs: &[&str]) -> String { - use sha2::{Digest, Sha256}; - let mut hasher = Sha256::new(); - for input in inputs { - hasher.update(input); - } - format!("{:x}", hasher.finalize()) -} - -fn wasm_to_ir(wasm_bytes: &[u8], session: &Session) -> miden_hir::Module { - use miden_hir_transform as transforms; - let mut ir_module = translate_module( - wasm_bytes, - &WasmTranslationConfig::default(), - &session.diagnostics, - ) - .expect("Failed to translate Wasm to IR module"); - - let mut analyses = AnalysisManager::new(); - let mut rewrites = RewriteSet::default(); - rewrites.push(ModuleRewritePassAdapter::new( - transforms::SplitCriticalEdges, - )); - rewrites.push(ModuleRewritePassAdapter::new(transforms::Treeify)); - rewrites.push(ModuleRewritePassAdapter::new(transforms::InlineBlocks)); - rewrites - .apply(&mut ir_module, &mut analyses, session) - .expect("Failed to apply rewrites"); - ir_module -} diff --git a/tests/integration/src/exec_emulator.rs b/tests/integration/src/exec_emulator.rs deleted file mode 100644 index a977196ae..000000000 --- a/tests/integration/src/exec_emulator.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::sync::Arc; - -use miden_codegen_masm::Emulator; -use miden_codegen_masm::Program; -use miden_hir::Felt; -use miden_hir::Stack; - -use crate::felt_conversion::TestFelt; - -/// Execute the module using the emulator with the given arguments -pub fn execute_emulator(program: Arc, args: &[Felt]) -> Vec { - let mut emulator = Emulator::default(); - emulator - .load_program(program) - .expect("failed to load program"); - { - let stack = emulator.stack_mut(); - for arg in args.iter().copied().rev() { - stack.push(arg); - } - } - let operand_stack = emulator.start().expect("failed to invoke"); - operand_stack - .stack() - .iter() - .map(|felt| TestFelt(felt.clone())) - .collect() -} diff --git a/tests/integration/src/exec_vm.rs b/tests/integration/src/exec_vm.rs deleted file mode 100644 index 23fcf687d..000000000 --- a/tests/integration/src/exec_vm.rs +++ /dev/null @@ -1,25 +0,0 @@ -use miden_core::Program; -use miden_core::StackInputs; -use miden_hir::Felt; -use miden_processor::DefaultHost; -use miden_processor::ExecutionOptions; - -use crate::felt_conversion::TestFelt; - -/// Execute the module using the VM with the given arguments -pub fn execute_vm(program: &Program, args: &[Felt]) -> Vec { - let stack_inputs = StackInputs::new(args.to_vec()); - let trace = miden_processor::execute( - program, - stack_inputs, - DefaultHost::default(), - ExecutionOptions::default(), - ) - .expect("failed to execute program on VM"); - trace - .stack_outputs() - .stack() - .into_iter() - .map(|i| TestFelt(i.clone().into())) - .collect() -} diff --git a/tests/integration/src/felt_conversion.rs b/tests/integration/src/felt_conversion.rs deleted file mode 100644 index bfbb17404..000000000 --- a/tests/integration/src/felt_conversion.rs +++ /dev/null @@ -1,116 +0,0 @@ -use miden_core::Felt; -use miden_core::StarkField; - -/// Wrapper around `Felt` that implements `From` for a bunch of types that are want to support in tests -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct TestFelt(pub Felt); - -impl From for TestFelt { - fn from(b: bool) -> Self { - Self(Felt::from(b as u32)) - } -} - -impl From for TestFelt { - fn from(t: u8) -> Self { - Self(t.into()) - } -} - -impl From for TestFelt { - fn from(t: i8) -> Self { - Self((t as u8).into()) - } -} - -impl From for TestFelt { - fn from(t: i16) -> Self { - Self((t as u16).into()) - } -} - -impl From for TestFelt { - fn from(t: u16) -> Self { - Self(t.into()) - } -} - -impl From for TestFelt { - fn from(t: i32) -> Self { - Self((t as u32).into()) - } -} - -impl From for TestFelt { - fn from(t: u32) -> Self { - Self(t.into()) - } -} - -impl From for TestFelt { - fn from(t: u64) -> Self { - Self(t.into()) - } -} - -impl From for TestFelt { - fn from(t: i64) -> Self { - Self((t as u64).into()) - } -} - -// Reverse TestFelt to Rust types conversion - -impl From for bool { - fn from(f: TestFelt) -> Self { - f.0.as_int() != 0 - } -} - -impl From for u8 { - fn from(f: TestFelt) -> Self { - f.0.as_int() as u8 - } -} - -impl From for i8 { - fn from(f: TestFelt) -> Self { - f.0.as_int() as i8 - } -} - -impl From for u16 { - fn from(f: TestFelt) -> Self { - f.0.as_int() as u16 - } -} - -impl From for i16 { - fn from(f: TestFelt) -> Self { - f.0.as_int() as i16 - } -} - -impl From for u32 { - fn from(f: TestFelt) -> Self { - f.0.as_int() as u32 - } -} - -impl From for i32 { - fn from(f: TestFelt) -> Self { - f.0.as_int() as i32 - } -} - -impl From for u64 { - fn from(f: TestFelt) -> Self { - f.0.as_int() as u64 - } -} - -impl From for i64 { - fn from(f: TestFelt) -> Self { - f.0.as_int() as i64 - } -} diff --git a/tests/integration/src/lib.rs b/tests/integration/src/lib.rs deleted file mode 100644 index 29be514f3..000000000 --- a/tests/integration/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Compilation and semantic tests for the whole compiler pipeline - -#![deny(warnings)] -#![deny(missing_docs)] - -mod compiler_test; -mod exec_emulator; -mod exec_vm; -pub(crate) mod felt_conversion; - -pub use compiler_test::default_session; -pub use compiler_test::CompilerTest; -pub use exec_emulator::execute_emulator; -pub use exec_vm::execute_vm; - -#[cfg(test)] -mod rust_masm_tests; diff --git a/tests/integration/src/rust_masm_tests/apps.rs b/tests/integration/src/rust_masm_tests/apps.rs deleted file mode 100644 index 8e4c187e8..000000000 --- a/tests/integration/src/rust_masm_tests/apps.rs +++ /dev/null @@ -1,42 +0,0 @@ -use expect_test::expect_file; -use miden_hir::Felt; -use proptest::prelude::*; -use proptest::test_runner::TestRunner; - -use crate::execute_emulator; -use crate::execute_vm; -use crate::CompilerTest; - -#[test] -fn fib() { - let mut test = - CompilerTest::rust_source_cargo("fib", "miden_integration_tests_rust_fib_wasm", "fib"); - // Test expected compilation artifacts - test.expect_wasm(expect_file!["../../expected/fib.wat"]); - test.expect_ir(expect_file!["../../expected/fib.hir"]); - test.expect_masm(expect_file!["../../expected/fib.masm"]); - let ir_masm = test.ir_masm_program(); - let vm_program = &test.vm_masm_program(); - - // Run the Rust and compiled MASM code against a bunch of random inputs and compare the results - TestRunner::default() - .run(&(1u32..30), move |a| { - let rust_out = miden_integration_tests_rust_fib::fib(a); - let mut args = [Felt::from(a)]; - let vm_out: u32 = execute_vm(&vm_program, &args) - .first() - .unwrap() - .clone() - .into(); - prop_assert_eq!(rust_out, vm_out); - args.reverse(); - let emul_out: u32 = execute_emulator(ir_masm.clone(), &args) - .first() - .unwrap() - .clone() - .into(); - prop_assert_eq!(rust_out, emul_out); - Ok(()) - }) - .unwrap(); -} diff --git a/tests/integration/src/rust_masm_tests/components.rs b/tests/integration/src/rust_masm_tests/components.rs deleted file mode 100644 index 58966f1bf..000000000 --- a/tests/integration/src/rust_masm_tests/components.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::compiler_test::default_session; -use crate::CompilerTest; -use expect_test::expect_file; -use miden_frontend_wasm::translate_component; -use miden_frontend_wasm::WasmTranslationConfig; - -#[ignore = "until Wasm component translation is implemented"] -#[test] -fn wcm_add() { - let test = CompilerTest::rust_source_cargo_component("add-comp"); - let artifact_name = test.source.artifact_name(); - test.expect_wasm(expect_file![format!( - "../../expected/components/{artifact_name}.wat" - )]); - test.expect_wit_bind(expect_file![format!( - "../../expected/components/bindings/{artifact_name}_bindings.rs" - )]); - let wasm_bytes = test.wasm_bytes; - - let session = default_session(); - let _ir_module = translate_component( - &wasm_bytes, - WasmTranslationConfig::default(), - &session.diagnostics, - ) - .expect("Failed to translate Wasm to IR module"); -} diff --git a/tests/integration/src/rust_masm_tests/instructions.rs b/tests/integration/src/rust_masm_tests/instructions.rs deleted file mode 100644 index 5c673d3a1..000000000 --- a/tests/integration/src/rust_masm_tests/instructions.rs +++ /dev/null @@ -1,339 +0,0 @@ -use std::sync::Arc; - -use crate::felt_conversion::TestFelt; -use expect_test::expect_file; -use miden_core::Felt; -use proptest::prelude::*; -use proptest::test_runner::TestError; -use proptest::test_runner::TestRunner; - -use crate::execute_emulator; -use crate::execute_vm; -use crate::CompilerTest; - -macro_rules! test_bin_op { - ($name:ident, $op:tt, $op_ty:tt, $res_ty:tt) => { - concat_idents::concat_idents!(test_name = $name, _, $op_ty { - #[test] - fn test_name() { - let op_str = stringify!($op); - let op_ty_str = stringify!($op_ty); - 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 mut test = CompilerTest::rust_fn_body(&main_fn); - // Test expected compilation artifacts - let artifact_name = format!("{}_{}", stringify!($name), stringify!($op_ty)); - 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_masm = test.ir_masm_program(); - let vm_program = test.vm_masm_program(); - - // Run the Rust and compiled MASM code against a bunch of random inputs and compare the results - let res = TestRunner::default() - .run(&(any::<$op_ty>(), any::<$op_ty>()), move |(a, b)| { - let rust_out = a $op b; - dbg!(&rust_out); - let args = [TestFelt::from(a).0, TestFelt::from(b).0]; - run_masm(rust_out, &vm_program, ir_masm.clone(), &args) - }); - match res { - Err(TestError::Fail(_, value)) => { - println!("Found minimal(shrinked) failing case: {:?}", value); - }, - Ok(_) => (), - _ => panic!("Unexpected test result: {:?}", res), - } - } - }); - }; -} - -macro_rules! test_unary_op { - ($name:ident, $op:tt, $op_ty:tt) => { - concat_idents::concat_idents!(test_name = $name, _, $op_ty { - #[test] - fn test_name() { - let op_str = stringify!($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); - // Test expected compilation artifacts - let artifact_name = format!("{}_{}", stringify!($name), stringify!($op_ty)); - 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_masm = test.ir_masm_program(); - let vm_program = test.vm_masm_program(); - - // Run the Rust and compiled MASM code against a bunch of random inputs and compare the results - let res = TestRunner::default() - .run(&(any::<$op_ty>()), move |a| { - let rust_out = $op a; - dbg!(&rust_out); - let args = [TestFelt::from(a).0]; - run_masm(rust_out, &vm_program, ir_masm.clone(), &args) - }); - match res { - Err(TestError::Fail(_, value)) => { - println!("Found minimal(shrinked) failing case: {:?}", value); - }, - Ok(_) => (), - _ => panic!("Unexpected test result: {:?}", res), - } - } - }); - }; -} - -#[allow(unused_macros)] -macro_rules! test_func_two_arg { - ($name:ident, $func:path, $a_ty:tt, $b_ty:tt, $res_ty:tt) => { - concat_idents::concat_idents!(test_name = $name, _, $a_ty, _, $b_ty { - #[test] - fn test_name() { - let func_name_str = stringify!($func); - let a_ty_str = stringify!($a_ty); - 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); - // 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")]); - test.expect_ir(expect_file![format!("../../expected/{artifact_name}.hir")]); - test.expect_masm(expect_file![format!("../../expected/{artifact_name}.masm")]); - let ir_masm = test.ir_masm_program(); - let vm_program = test.vm_masm_program(); - - // Run the Rust and compiled MASM code against a bunch of random inputs and compare the results - let res = TestRunner::default() - .run(&(any::<$a_ty>(), any::<$b_ty>()), move |(a, b)| { - let rust_out = $func(a, b); - dbg!(&rust_out); - let args = [TestFelt::from(a).0, TestFelt::from(b).0]; - run_masm(rust_out, &vm_program, ir_masm.clone(), &args) - }); - match res { - Err(TestError::Fail(_, value)) => { - println!("Found minimal(shrinked) failing case: {:?}", value); - }, - Ok(_) => (), - _ => panic!("Unexpected test result: {:?}", res), - } - } - }); - }; -} -fn run_masm( - rust_out: T, - vm_program: &miden_core::Program, - ir_masm: Arc, - args: &[Felt], -) -> Result<(), TestCaseError> -where - T: Clone + From + std::cmp::PartialEq + std::fmt::Debug, -{ - let vm_out: T = execute_vm(&vm_program, &args) - .first() - .unwrap() - .clone() - .into(); - prop_assert_eq!(rust_out.clone(), vm_out, "VM output mismatch"); - let emul_out: T = execute_emulator(ir_masm.clone(), &args) - .first() - .unwrap() - .clone() - .into(); - prop_assert_eq!(rust_out, emul_out, "Emulator output mismatch"); - Ok(()) -} - -macro_rules! test_bool_op { - ($name:ident, $op:tt, $op_ty:tt) => { - test_bin_op!($name, $op, $op_ty, bool); - }; -} - -#[allow(unused_macros)] -macro_rules! test_int_op { - ($name:ident, $op:tt, $op_ty:tt) => { - test_bin_op!($name, $op, $op_ty, $op_ty); - }; -} - -// 64-bit ops are not implemented yet -// test_bool_op!(ge, >=, u64); -// test_bool_op!(ge, >=, i64); -// test_bool_op!(gt, >, u64); -// test_bool_op!(gt, >, i64); -// test_bool_op!(le, <=, u64); -// test_bool_op!(le, <=, i64); -// test_bool_op!(lt, <, u64); -// test_bool_op!(lt, <, i64); -// test_int_op!(add, +, u64); -// test_int_op!(add, +, i64); -// test_int_op!(sub, -, u64); -// test_int_op!(sub, -, i64); -// test_int_op!(mul, *, u64); -// test_int_op!(mul, *, i64); -// test_int_op!(div, /, u64); -// test_int_op!(div, /, i64); -// test_int_op!(rem, %, u64); -// test_int_op!(rem, %, i64); -// test_unary_op!(neg, -, u64); -// test_unary_op!(neg, -, i64); -// test_unary_op!(not, !, u64); -// test_unary_op!(not, !, i64); -// test_int_op!(shl, <<, u64); -// test_int_op!(shl, <<, i64); -// test_int_op!(shr, >>, u64); -// test_int_op!(shr, >>, i64); -// test_unary_op!(neg, -, i64); - -// MASM compilation error (missing import for intrinsic) -// -// Comparison ops -// -// test_bool_op!(ge, >=, i32); -// test_bool_op!(ge, >=, i16); -// test_bool_op!(ge, >=, i8); -// -// test_bool_op!(gt, >, i32); -// test_bool_op!(gt, >, i16); -// test_bool_op!(gt, >, i8); -// -// test_bool_op!(le, <=, i32); -// test_bool_op!(le, <=, i16); -// test_bool_op!(le, <=, i8); -// -// test_bool_op!(lt, <, i32); -// test_bool_op!(lt, <, i16); -// test_bool_op!(lt, <, i8); -// -// Arithmetic ops -// -// test_int_op!(mul, *, u32); -// test_int_op!(mul, *, u16); -// test_int_op!(mul, *, u8); -// test_int_op!(mul, *, i32); -// test_int_op!(mul, *, i16); -// test_int_op!(mul, *, i8); -// -// Bitwise ops -// -// test_int_op!(shr, >>, i8); -// test_int_op!(shr, >>, i16); -// test_int_op!(shr, >>, i32); -// -// test_unary_op!(not, !, u64); -// test_unary_op!(not, !, i64); - -// stdlib is not linked (missing import for stdlib) -// test_int_op!(and, &, u64); -// test_int_op!(and, &, i64); -// test_int_op!(or, |, u64); -// test_int_op!(or, |, i64); -// test_int_op!(xor, ^, u64); -// test_int_op!(xor, ^, i64); - -// TODO: build with cargo to avoid core::panicking -// TODO: separate macro for div and rem tests to filter out division by zero -// test_int_op!(div, /, u32); -// ... -// add tests for div, rem, - -// enable when https://github.com/0xPolygonMiden/compiler/issues/56 is fixed -//test_func_two_arg!(min, core::cmp::min, i32, i32, i32); -// test_func_two_arg!(min, core::cmp::min, u32, u32, u32); -// test_func_two_arg!(min, core::cmp::min, u8, u8, u8); -// test_func_two_arg!(max, core::cmp::max, u8, u8, u8); - -test_bool_op!(ge, >=, u32); -test_bool_op!(ge, >=, u16); -test_bool_op!(ge, >=, u8); - -test_bool_op!(gt, >, u32); -test_bool_op!(gt, >, u16); -test_bool_op!(gt, >, u8); - -test_bool_op!(le, <=, u32); -test_bool_op!(le, <=, u16); -test_bool_op!(le, <=, u8); - -test_bool_op!(lt, <, u32); -test_bool_op!(lt, <, u16); -test_bool_op!(lt, <, u8); - -test_bool_op!(eq, ==, u64); -test_bool_op!(eq, ==, u32); -test_bool_op!(eq, ==, u16); -test_bool_op!(eq, ==, u8); -test_bool_op!(eq, ==, i64); -test_bool_op!(eq, ==, i32); -test_bool_op!(eq, ==, i16); -test_bool_op!(eq, ==, i8); - -test_int_op!(add, +, u32); -test_int_op!(add, +, u16); -test_int_op!(add, +, u8); -test_int_op!(add, +, i32); -test_int_op!(add, +, i16); -test_int_op!(add, +, i8); - -test_int_op!(sub, -, u32); -test_int_op!(sub, -, u16); -test_int_op!(sub, -, u8); -test_int_op!(sub, -, i32); -test_int_op!(sub, -, i16); -test_int_op!(sub, -, i8); - -test_bool_op!(and, &&, bool); -test_bool_op!(or, ||, bool); -test_bool_op!(xor, ^, bool); - -test_int_op!(and, &, u8); -test_int_op!(and, &, u16); -test_int_op!(and, &, u32); -test_int_op!(and, &, i8); -test_int_op!(and, &, i16); -test_int_op!(and, &, i32); - -test_int_op!(or, |, u8); -test_int_op!(or, |, u16); -test_int_op!(or, |, u32); -test_int_op!(or, |, i8); -test_int_op!(or, |, i16); -test_int_op!(or, |, i32); - -test_int_op!(xor, ^, u8); -test_int_op!(xor, ^, u16); -test_int_op!(xor, ^, u32); -test_int_op!(xor, ^, i8); -test_int_op!(xor, ^, i16); -test_int_op!(xor, ^, i32); - -test_int_op!(shl, <<, u8); -test_int_op!(shl, <<, u16); -test_int_op!(shl, <<, u32); -test_int_op!(shl, <<, i8); -test_int_op!(shl, <<, i16); -test_int_op!(shl, <<, i32); - -test_int_op!(shr, >>, u8); -test_int_op!(shr, >>, u16); -test_int_op!(shr, >>, u32); - -test_unary_op!(neg, -, i32); -test_unary_op!(neg, -, i16); -test_unary_op!(neg, -, i8); - -test_unary_op!(not, !, i32); -test_unary_op!(not, !, i16); -test_unary_op!(not, !, i8); -test_unary_op!(not, !, u32); -test_unary_op!(not, !, u16); -test_unary_op!(not, !, u8); - -test_unary_op!(not, !, bool); diff --git a/tests/integration/src/rust_masm_tests/mod.rs b/tests/integration/src/rust_masm_tests/mod.rs deleted file mode 100644 index 9450be1ac..000000000 --- a/tests/integration/src/rust_masm_tests/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod apps; -mod components; -mod instructions; -mod sdk; diff --git a/tests/integration/src/rust_masm_tests/sdk.rs b/tests/integration/src/rust_masm_tests/sdk.rs deleted file mode 100644 index 9a080874c..000000000 --- a/tests/integration/src/rust_masm_tests/sdk.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::CompilerTest; -use expect_test::expect_file; - -#[test] -fn sdk() { - let test = CompilerTest::rust_source_cargo_component("sdk/sdk"); - let artifact_name = test.source.artifact_name(); - test.expect_wasm(expect_file![format!( - "../../expected/sdk_basic_wallet/{artifact_name}.wat" - )]); -} - -#[test] -fn sdk_basic_wallet() { - let test = CompilerTest::rust_source_cargo_component("sdk/basic-wallet"); - let artifact_name = test.source.artifact_name(); - test.expect_wasm(expect_file![format!( - "../../expected/sdk_basic_wallet/{artifact_name}.wat" - )]); - test.expect_wit_bind(expect_file![format!( - "../../expected/sdk_basic_wallet/bindings/{artifact_name}_bindings.rs" - )]); -} - -#[test] -fn sdk_basic_wallet_p2id_note() { - let test = CompilerTest::rust_source_cargo_component("sdk/p2id-note"); - let artifact_name = test.source.artifact_name(); - test.expect_wasm(expect_file![format!( - "../../expected/sdk_basic_wallet/{artifact_name}.wat" - )]); - test.expect_wit_bind(expect_file![format!( - "../../expected/sdk_basic_wallet/bindings/{artifact_name}_bindings.rs" - )]); -} diff --git a/tests/lit/.gitkeep b/tests/lit/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/rust-apps-wasm/add-comp/Cargo.lock b/tests/rust-apps-wasm/add-comp/Cargo.lock deleted file mode 100644 index de5d15d38..000000000 --- a/tests/rust-apps-wasm/add-comp/Cargo.lock +++ /dev/null @@ -1,81 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adder-wasm-component" -version = "0.1.0" -dependencies = [ - "cargo-component-bindings", -] - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "cargo-component-bindings" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3cf2350ce4e1aaf711114952eb135081d936b76da37f3fe09e87f0a28cabfa" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9149c8ce53e8348be7b673f09a89503077721e9ff6321761d3bba40abefd7b2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wit-bindgen" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0205c6e67438f9d657318e0d7ee407a8017cd7bc5f1636cd4a280d4ccbc8d4a0" -dependencies = [ - "bitflags", -] diff --git a/tests/rust-apps-wasm/add-comp/Cargo.toml b/tests/rust-apps-wasm/add-comp/Cargo.toml deleted file mode 100644 index a21f5dd0a..000000000 --- a/tests/rust-apps-wasm/add-comp/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "adder-wasm-component" -version = "0.1.0" -rust-version = "1.71" -authors = ["Miden Team"] -license = "MIT" -edition = "2021" -publish = false - -[dependencies] -cargo-component-bindings = "0.5.0" - -[lib] -crate-type = ["cdylib"] - -[package.metadata.component] -package = "miden:adder" - -[profile.release] -panic = "abort" \ No newline at end of file diff --git a/tests/rust-apps-wasm/add-comp/src/lib.rs b/tests/rust-apps-wasm/add-comp/src/lib.rs deleted file mode 100644 index c7e78f239..000000000 --- a/tests/rust-apps-wasm/add-comp/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_std] - -cargo_component_bindings::generate!(); - -use crate::bindings::Guest; - -struct Component; - -impl Guest for Component { - fn add(a: i32, b: i32) -> i32 { - a + b - } -} diff --git a/tests/rust-apps-wasm/add-comp/wit/adder.wit b/tests/rust-apps-wasm/add-comp/wit/adder.wit deleted file mode 100644 index d49970358..000000000 --- a/tests/rust-apps-wasm/add-comp/wit/adder.wit +++ /dev/null @@ -1,5 +0,0 @@ -package miden:adder@1.0.0; - -world adder { - export add: func(a: s32, b: s32) -> s32; -} diff --git a/tests/rust-apps-wasm/fib/Cargo.lock b/tests/rust-apps-wasm/fib/Cargo.lock deleted file mode 100644 index f5791d911..000000000 --- a/tests/rust-apps-wasm/fib/Cargo.lock +++ /dev/null @@ -1,30 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "dlmalloc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" -dependencies = [ - "libc", -] - -[[package]] -name = "libc" -version = "0.2.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" - -[[package]] -name = "miden-integration-tests-rust-fib" -version = "0.1.0" - -[[package]] -name = "miden-integration-tests-rust-fib-wasm" -version = "0.1.0" -dependencies = [ - "dlmalloc", - "miden-integration-tests-rust-fib", -] diff --git a/tests/rust-apps-wasm/fib/Cargo.toml b/tests/rust-apps-wasm/fib/Cargo.toml deleted file mode 100644 index d52bc7c52..000000000 --- a/tests/rust-apps-wasm/fib/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "miden-integration-tests-rust-fib-wasm" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -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 diff --git a/tests/rust-apps-wasm/fib/src/lib.rs b/tests/rust-apps-wasm/fib/src/lib.rs deleted file mode 100644 index 357025f8d..000000000 --- a/tests/rust-apps-wasm/fib/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_std] - -#[global_allocator] -static A: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc; - -#[panic_handler] -fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -pub fn fib(n: u32) -> u32 { - miden_integration_tests_rust_fib::fib(n) -} diff --git a/tests/rust-apps-wasm/sdk/basic-wallet/Cargo.lock b/tests/rust-apps-wasm/sdk/basic-wallet/Cargo.lock deleted file mode 100644 index 45916307c..000000000 --- a/tests/rust-apps-wasm/sdk/basic-wallet/Cargo.lock +++ /dev/null @@ -1,81 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "basic-wallet" -version = "0.1.0" -dependencies = [ - "cargo-component-bindings", -] - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "cargo-component-bindings" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545e48ba821e07f93c97aea897bee6d407de4d58947f914160131f3d78b2c704" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e198ee0b668e902b43b5e7d2e9620a3891d2632429b3ba66e1ceea455053cbf5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wit-bindgen" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76f1d099678b4f69402a421e888bbe71bf20320c2f3f3565d0e7484dbe5bc20" -dependencies = [ - "bitflags", -] diff --git a/tests/rust-apps-wasm/sdk/basic-wallet/Cargo.toml b/tests/rust-apps-wasm/sdk/basic-wallet/Cargo.toml deleted file mode 100644 index 5c385ee9a..000000000 --- a/tests/rust-apps-wasm/sdk/basic-wallet/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "basic-wallet" -version = "0.1.0" -rust-version = "1.71" -authors = ["Miden Team"] -description = "Basic wallet" -repository = "https://github.com/0xPolygonMiden/miden-ir" -homepage = "https://github.com/0xPolygonMiden/miden-ir" -documentation = "https://github.com/0xPolygonMiden/miden-ir" -license = "MIT" -edition = "2021" -publish = false - -[dependencies] -cargo-component-bindings = "0.6.0" - -[lib] -crate-type = ["cdylib"] - -[package.metadata.component] -package = "miden:basic-wallet" - -[package.metadata.component.dependencies] - -[package.metadata.component.target.dependencies] -"miden:base" = { path = "../sdk/wit" } - -[profile.release] -panic = "abort" \ No newline at end of file diff --git a/tests/rust-apps-wasm/sdk/basic-wallet/src/lib.rs b/tests/rust-apps-wasm/sdk/basic-wallet/src/lib.rs deleted file mode 100644 index 1982653ef..000000000 --- a/tests/rust-apps-wasm/sdk/basic-wallet/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![no_std] - -cargo_component_bindings::generate!(); - -use bindings::exports::miden::basic_wallet::basic_wallet::{Asset, Guest, Recipient, Tag}; -use bindings::miden::base::tx_kernel::{add_asset, create_note, remove_asset}; - -struct Component; - -impl Guest for Component { - fn receive_asset(asset: Asset) { - add_asset(asset); - } - - fn send_asset(asset: Asset, tag: Tag, recipient: Recipient) { - let asset = remove_asset(asset); - create_note(asset, tag, recipient); - } -} diff --git a/tests/rust-apps-wasm/sdk/basic-wallet/wit/basic-wallet.wit b/tests/rust-apps-wasm/sdk/basic-wallet/wit/basic-wallet.wit deleted file mode 100644 index 8fe44e518..000000000 --- a/tests/rust-apps-wasm/sdk/basic-wallet/wit/basic-wallet.wit +++ /dev/null @@ -1,18 +0,0 @@ -package miden:basic-wallet@1.0.0; - -use miden:base/types@1.0.0; -use miden:base/tx-kernel@1.0.0; -use miden:base/account@1.0.0; - -interface basic-wallet { - use types.{asset, tag, recipient}; - - receive-asset: func(asset: asset); - send-asset: func(asset: asset, tag: tag, recipient: recipient); -} - -world basic-wallet-world { - import tx-kernel; - export account; - export basic-wallet; -} diff --git a/tests/rust-apps-wasm/sdk/p2id-note/Cargo.lock b/tests/rust-apps-wasm/sdk/p2id-note/Cargo.lock deleted file mode 100644 index d06b1e0bd..000000000 --- a/tests/rust-apps-wasm/sdk/p2id-note/Cargo.lock +++ /dev/null @@ -1,81 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "basic-wallet-p2id-note" -version = "0.1.0" -dependencies = [ - "cargo-component-bindings", -] - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "cargo-component-bindings" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545e48ba821e07f93c97aea897bee6d407de4d58947f914160131f3d78b2c704" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e198ee0b668e902b43b5e7d2e9620a3891d2632429b3ba66e1ceea455053cbf5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wit-bindgen" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76f1d099678b4f69402a421e888bbe71bf20320c2f3f3565d0e7484dbe5bc20" -dependencies = [ - "bitflags", -] diff --git a/tests/rust-apps-wasm/sdk/p2id-note/Cargo.toml b/tests/rust-apps-wasm/sdk/p2id-note/Cargo.toml deleted file mode 100644 index db445cb67..000000000 --- a/tests/rust-apps-wasm/sdk/p2id-note/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "basic-wallet-p2id-note" -version = "0.1.0" -rust-version = "1.71" -authors = ["Miden Team"] -description = "p2id-note for basic wallet" -repository = "https://github.com/0xPolygonMiden/miden-ir" -homepage = "https://github.com/0xPolygonMiden/miden-ir" -documentation = "https://github.com/0xPolygonMiden/miden-ir" -license = "MIT" -edition = "2021" -publish = false - -[lib] -crate-type = ["cdylib"] - -[dependencies] -cargo-component-bindings = "0.6.0" - -[package.metadata.component] -package = "miden:basic-wallet-p2id-note" - -[package.metadata.component.dependencies] - -[package.metadata.component.target.dependencies] -"miden:base" = { path = "../sdk/wit" } -"miden:basic-wallet" = { path = "../basic-wallet/wit" } - -[profile.release] -panic = "abort" \ No newline at end of file diff --git a/tests/rust-apps-wasm/sdk/p2id-note/src/lib.rs b/tests/rust-apps-wasm/sdk/p2id-note/src/lib.rs deleted file mode 100644 index 2a3904e6a..000000000 --- a/tests/rust-apps-wasm/sdk/p2id-note/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![no_std] - -cargo_component_bindings::generate!(); - -use bindings::miden::base::tx_kernel::{get_assets, get_id, get_inputs}; -use bindings::miden::basic_wallet::basic_wallet::receive_asset; - -use bindings::exports::miden::base::note::Guest; - -pub struct Component; - -impl Guest for Component { - fn note_script() { - let inputs = get_inputs(); - let target_account_id = inputs[0]; - let account_id = get_id(); - assert_eq!(account_id, target_account_id); - let assets = get_assets(); - for asset in assets { - receive_asset(asset); - } - } -} diff --git a/tests/rust-apps-wasm/sdk/p2id-note/wit/p2id-note.wit b/tests/rust-apps-wasm/sdk/p2id-note/wit/p2id-note.wit deleted file mode 100644 index 8fb846bc8..000000000 --- a/tests/rust-apps-wasm/sdk/p2id-note/wit/p2id-note.wit +++ /dev/null @@ -1,9 +0,0 @@ -package miden:p2id@1.0.0; - - -world notes-world { - import miden:base/tx-kernel@1.0.0; - import miden:basic-wallet/basic-wallet@1.0.0; - - export miden:base/note@1.0.0; -} diff --git a/tests/rust-apps-wasm/sdk/sdk/Cargo.lock b/tests/rust-apps-wasm/sdk/sdk/Cargo.lock deleted file mode 100644 index 74b0438ac..000000000 --- a/tests/rust-apps-wasm/sdk/sdk/Cargo.lock +++ /dev/null @@ -1,81 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "cargo-component-bindings" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545e48ba821e07f93c97aea897bee6d407de4d58947f914160131f3d78b2c704" -dependencies = [ - "cargo-component-macro", - "wit-bindgen", -] - -[[package]] -name = "cargo-component-macro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e198ee0b668e902b43b5e7d2e9620a3891d2632429b3ba66e1ceea455053cbf5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "miden-sdk" -version = "0.1.0" -dependencies = [ - "cargo-component-bindings", -] - -[[package]] -name = "proc-macro2" -version = "1.0.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wit-bindgen" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76f1d099678b4f69402a421e888bbe71bf20320c2f3f3565d0e7484dbe5bc20" -dependencies = [ - "bitflags", -] diff --git a/tests/rust-apps-wasm/sdk/sdk/Cargo.toml b/tests/rust-apps-wasm/sdk/sdk/Cargo.toml deleted file mode 100644 index 9c3decec1..000000000 --- a/tests/rust-apps-wasm/sdk/sdk/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "miden-sdk" -version = "0.1.0" -rust-version = "1.71" -authors = ["Miden Team"] -description = "Miden SDK" -license = "MIT" -edition = "2021" -publish = false - -# To keep it out of the root workspace since it cannot be built for a non-Wasm target -[workspace] - -[dependencies] -cargo-component-bindings = "0.6.0" - -[lib] -crate-type = ["cdylib"] - -[package.metadata.component] -package = "component:miden" - -[package.metadata.component.dependencies] - -[profile.release] -panic = "abort" \ No newline at end of file diff --git a/tests/rust-apps-wasm/sdk/sdk/src/lib.rs b/tests/rust-apps-wasm/sdk/sdk/src/lib.rs deleted file mode 100644 index 1758058dc..000000000 --- a/tests/rust-apps-wasm/sdk/sdk/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![no_std] - -cargo_component_bindings::generate!(); diff --git a/tests/rust-apps-wasm/sdk/sdk/wit/miden.wit b/tests/rust-apps-wasm/sdk/sdk/wit/miden.wit deleted file mode 100644 index 00a3a5e9d..000000000 --- a/tests/rust-apps-wasm/sdk/sdk/wit/miden.wit +++ /dev/null @@ -1,132 +0,0 @@ -package miden:base@1.0.0; - -interface types { - /// Represents base field element in the field using Montgomery representation. - /// Internal values represent x * R mod M where R = 2^64 mod M and x in [0, M). - /// The backing type is `u64` but the internal values are always in the range [0, M). - /// Field modulus M = 2^64 - 2^32 + 1 - type felt = u64; - /// A group of four field elements in the Miden base field. - type word = tuple; - - /// Unique identifier of an account. - /// - /// Account ID consists of 1 field element (~64 bits). This field element uniquely identifies a - /// single account and also specifies the type of the underlying account. Specifically: - /// - The two most significant bits of the ID specify the type of the account: - /// - 00 - regular account with updatable code. - /// - 01 - regular account with immutable code. - /// - 10 - fungible asset faucet with immutable code. - /// - 11 - non-fungible asset faucet with immutable code. - /// - The third most significant bit of the ID specifies whether the account data is stored on-chain: - /// - 0 - full account data is stored on-chain. - /// - 1 - only the account hash is stored on-chain which serves as a commitment to the account state. - /// As such the three most significant bits fully describes the type of the account. - type account-id = felt; - - /// Recipient of the note, i.e., hash(hash(hash(serial_num, [0; 4]), note_script_hash), input_hash) - type recipient = word; - - type tag = felt; - - /// A fungible asset - record fungible-asset { - /// Faucet ID of the faucet which issued the asset as well as the asset amount. - asset: account-id, - /// Asset amount is guaranteed to be 2^63 - 1 or smaller. - amount: u64 - } - - /// A commitment to a non-fungible asset. - /// - /// A non-fungible asset consists of 4 field elements which are computed by hashing asset data - /// (which can be of arbitrary length) to produce: [d0, d1, d2, d3]. We then replace d1 with the - /// faucet_id that issued the asset: [d0, faucet_id, d2, d3]. We then set the most significant bit - /// of the most significant element to ZERO. - type non-fungible-asset = word; - - /// A fungible or a non-fungible asset. - /// - /// All assets are encoded using a single word (4 elements) such that it is easy to determine the - /// type of an asset both inside and outside Miden VM. Specifically: - /// Element 1 will be: - /// - ZERO for a fungible asset - /// - non-ZERO for a non-fungible asset - /// The most significant bit will be: - /// - ONE for a fungible asset - /// - ZERO for a non-fungible asset - /// - /// The above properties guarantee that there can never be a collision between a fungible and a - /// non-fungible asset. - /// - /// The methodology for constructing fungible and non-fungible assets is described below. - /// - /// # Fungible assets - /// The most significant element of a fungible asset is set to the ID of the faucet which issued - /// the asset. This guarantees the properties described above (the first bit is ONE). - /// - /// The least significant element is set to the amount of the asset. This amount cannot be greater - /// than 2^63 - 1 and thus requires 63-bits to store. - /// - /// Elements 1 and 2 are set to ZERO. - /// - /// It is impossible to find a collision between two fungible assets issued by different faucets as - /// the faucet_id is included in the description of the asset and this is guaranteed to be different - /// for each faucet as per the faucet creation logic. - /// - /// # Non-fungible assets - /// The 4 elements of non-fungible assets are computed as follows: - /// - First the asset data is hashed. This compresses an asset of an arbitrary length to 4 field - /// elements: [d0, d1, d2, d3]. - /// - d1 is then replaced with the faucet_id which issues the asset: [d0, faucet_id, d2, d3]. - /// - Lastly, the most significant bit of d3 is set to ZERO. - /// - /// It is impossible to find a collision between two non-fungible assets issued by different faucets - /// as the faucet_id is included in the description of the non-fungible asset and this is guaranteed - /// to be different as per the faucet creation logic. Collision resistance for non-fungible assets - /// issued by the same faucet is ~2^95. - variant asset { - fungible(fungible-asset), - non-fungible(non-fungible-asset), - } - - /// Inputs of the currently executed note, never exceeds 16 felts - type note-inputs = list; -} - -interface tx-kernel { - use types.{asset, tag, recipient, note-inputs, account-id}; - - // Account-related functions - - /// Get the id of the currently executing account - get-id: func() -> account-id; - /// Add the specified asset to the vault - add-asset: func(asset: asset) -> asset; - /// Remove the specified asset from the vault - remove-asset: func(asset: asset) -> asset; - - - // Note-related functions - - /// Creates a new note. - /// asset is the asset to be included in the note. - /// tag is the tag to be included in the note. - /// recipient is the recipient of the note. - create-note: func(asset: asset, tag: tag, recipient: recipient); - /// Get the inputs of the currently executed note - get-inputs: func() -> note-inputs; - /// Get the assets of the currently executing note - get-assets: func() -> list; -} - -/// Marker interface. Expected to be implemented by the account/wallet -interface account {} - -interface note { - note-script: func(); -} - -world base-world { - export types; -} diff --git a/tests/rust-apps/fib/Cargo.toml b/tests/rust-apps/fib/Cargo.toml deleted file mode 100644 index 3194545f0..000000000 --- a/tests/rust-apps/fib/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[package] -name = "miden-integration-tests-rust-fib" -version = "0.1.0" -edition = "2021" diff --git a/tests/rust-apps/fib/src/lib.rs b/tests/rust-apps/fib/src/lib.rs deleted file mode 100644 index d63aefa74..000000000 --- a/tests/rust-apps/fib/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_std] - -#[no_mangle] -pub fn fib(n: u32) -> u32 { - let mut a = 0; - let mut b = 1; - for _ in 0..n { - let c = a + b; - a = b; - b = c; - } - a -} diff --git a/tools/.gitignore b/tools/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/tools/cargo-miden/Cargo.toml b/tools/cargo-miden/Cargo.toml deleted file mode 100644 index 2f49bf45f..000000000 --- a/tools/cargo-miden/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "cargo-miden" -version.workspace = true -rust-version.workspace = true -authors.workspace = true -description.workspace = true -repository.workspace = true -homepage.workspace = true -documentation.workspace = true -categories.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -edition.workspace = true -publish.workspace = true -autotests = false # disable autodiscovery of tests - -[[bin]] -name = "cargo-miden" - -[[test]] -name = "integration" -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 -anyhow.workspace = true -cargo-component = "0.5" -cargo-component-core = "0.5" -cargo_metadata = "0.18" -cargo-generate = "0.18" -semver = "1.0.20" -parse_arg = "0.1.4" -path-absolutize = "3.1.1" - -[dev-dependencies] \ No newline at end of file diff --git a/tools/cargo-miden/README.md b/tools/cargo-miden/README.md deleted file mode 100644 index 3d0f6961d..000000000 --- a/tools/cargo-miden/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Miden Cargo Extension - -This crate provides a cargo extension that allows you to compile Rust code to Miden VM MASM. - -## Installation - -To install the extension, run: - -```bash -cargo install cargo-miden -``` - -## Usage - -### Getting help -To get help with the extension, run: - -```bash -cargo miden -``` - -Or for help with a specific command: - -```bash -cargo miden --help -``` - -### Creating a new project -To create a new Miden VM project, run: - -```bash -cargo miden new -``` - -### Compiling a project -To compile a Rust crate to Miden VM MASM, run: - -```bash -cargo miden build -``` - -Without any additional arguments, this will compile the library target in the target directory in the `miden` folder. diff --git a/tools/cargo-miden/src/build.rs b/tools/cargo-miden/src/build.rs deleted file mode 100644 index e072f787c..000000000 --- a/tools/cargo-miden/src/build.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::{ - path::{Path, PathBuf}, - sync::Arc, -}; - -use anyhow::{bail, Context}; -use miden_diagnostics::Verbosity; -use midenc_session::{ - InputFile, OutputFile, OutputType, OutputTypeSpec, OutputTypes, ProjectType, Session, TargetEnv, -}; - -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 - }; - - if !output_folder.exists() { - bail!( - "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 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) - .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 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/config.rs b/tools/cargo-miden/src/config.rs deleted file mode 100644 index 22eb9d2ee..000000000 --- a/tools/cargo-miden/src/config.rs +++ /dev/null @@ -1,809 +0,0 @@ -//! Module for cargo-miden configuration. -//! -//! This implements an argument parser because `clap` is not -//! designed for parsing unknown or unsupported arguments. -//! -//! See https://github.com/clap-rs/clap/issues/1404 for some -//! discussion around this issue. -//! -//! To properly "wrap" `cargo` commands, we need to be able to -//! detect certain arguments, but not error out if the arguments -//! are otherwise unknown as they will be passed to `cargo` directly. -//! -//! This will allow `cargo-miden` to be used as a drop-in -//! replacement for `cargo` without having to be fully aware of -//! the many subcommands and options that `cargo` supports. -//! -//! What is detected here is the minimal subset of the arguments -//! that `cargo` supports which are necessary for `cargo-miden` -//! to function. - -use anyhow::{anyhow, bail, Context, Result}; -use cargo_component_core::terminal::{Color, Terminal}; -use parse_arg::{iter_short, match_arg}; -use semver::Version; -use std::fmt; -use std::str::FromStr; -use std::{collections::BTreeMap, fmt::Display, path::PathBuf}; - -/// Represents a cargo package specifier. -/// -/// See `cargo help pkgid` for more information. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct CargoPackageSpec { - /// The name of the package, e.g. `foo`. - pub name: String, - /// The version of the package, if specified. - pub version: Option, -} - -impl CargoPackageSpec { - /// Creates a new package specifier from a string. - pub fn new(spec: impl Into) -> Result { - let spec = spec.into(); - - // Bail out if the package specifier contains a URL. - if spec.contains("://") { - bail!("URL package specifier `{spec}` is not supported"); - } - - Ok(match spec.split_once('@') { - Some((name, version)) => Self { - name: name.to_string(), - version: Some( - version - .parse() - .with_context(|| format!("invalid package specified `{spec}`"))?, - ), - }, - None => Self { - name: spec, - version: None, - }, - }) - } -} - -impl FromStr for CargoPackageSpec { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - Self::new(s) - } -} - -impl fmt::Display for CargoPackageSpec { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{name}", name = self.name)?; - if let Some(version) = &self.version { - write!(f, "@{version}")?; - } - - Ok(()) - } -} - -#[derive(Debug, Clone)] -enum Arg { - Flag { - name: &'static str, - short: Option, - value: bool, - }, - Single { - name: &'static str, - value_name: &'static str, - short: Option, - value: Option, - }, - Multiple { - name: &'static str, - value_name: &'static str, - short: Option, - values: Vec, - }, - Counting { - name: &'static str, - short: Option, - value: usize, - }, -} - -impl Arg { - fn name(&self) -> &'static str { - match self { - Self::Flag { name, .. } - | Self::Single { name, .. } - | Self::Multiple { name, .. } - | Self::Counting { name, .. } => name, - } - } - - fn short(&self) -> Option { - match self { - Self::Flag { short, .. } - | Self::Single { short, .. } - | Self::Multiple { short, .. } - | Self::Counting { short, .. } => *short, - } - } - - fn expects_value(&self) -> bool { - matches!(self, Self::Single { .. } | Self::Multiple { .. }) - } - - fn set_value(&mut self, v: String) -> Result<()> { - match self { - Self::Single { value, .. } => { - if value.is_some() { - bail!("the argument '{self}' cannot be used multiple times"); - } - - *value = Some(v); - Ok(()) - } - Self::Multiple { values, .. } => { - values.push(v); - Ok(()) - } - _ => unreachable!(), - } - } - - fn set_present(&mut self) -> Result<()> { - match self { - Self::Flag { value, .. } => { - if *value { - bail!("the argument '{self}' cannot be used multiple times"); - } - - *value = true; - Ok(()) - } - Self::Counting { value, .. } => { - *value += 1; - Ok(()) - } - _ => unreachable!(), - } - } - - fn take_single(&mut self) -> Option { - match self { - Self::Single { value, .. } => value.take(), - _ => None, - } - } - - fn take_multiple(&mut self) -> Vec { - match self { - Self::Multiple { values, .. } => std::mem::take(values), - _ => Vec::new(), - } - } - - fn count(&self) -> usize { - match self { - Arg::Flag { value, .. } => *value as usize, - Arg::Single { value, .. } => value.is_some() as usize, - Arg::Multiple { values, .. } => values.len(), - Arg::Counting { value, .. } => *value, - } - } - - #[cfg(test)] - fn reset(&mut self) { - match self { - Arg::Flag { value, .. } => *value = false, - Arg::Single { value, .. } => *value = None, - Arg::Multiple { values, .. } => values.clear(), - Arg::Counting { value, .. } => *value = 0, - } - } -} - -impl Display for Arg { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{name}", name = self.name())?; - match self { - Self::Single { value_name, .. } | Self::Multiple { value_name, .. } => { - write!(f, " <{value_name}>") - } - _ => Ok(()), - } - } -} - -#[derive(Default, Debug, Clone)] -struct Args { - args: Vec, - long: BTreeMap<&'static str, usize>, - short: BTreeMap, -} - -impl Args { - fn flag(self, name: &'static str, short: Option) -> Self { - self.insert(Arg::Flag { - name, - short, - value: false, - }) - } - - fn single(self, name: &'static str, value_name: &'static str, short: Option) -> Self { - self.insert(Arg::Single { - name, - value_name, - short, - value: None, - }) - } - - fn multiple(self, name: &'static str, value_name: &'static str, short: Option) -> Self { - self.insert(Arg::Multiple { - name, - value_name, - short, - values: Vec::new(), - }) - } - - fn counting(self, name: &'static str, short: Option) -> Self { - self.insert(Arg::Counting { - name, - short, - value: 0, - }) - } - - fn get(&mut self, name: &str) -> Option<&Arg> { - self.long.get(name).copied().map(|i| &self.args[i]) - } - - fn get_mut(&mut self, name: &str) -> Option<&mut Arg> { - self.long.get(name).copied().map(|i| &mut self.args[i]) - } - - fn get_short_mut(&mut self, short: char) -> Option<&mut Arg> { - self.short.get(&short).copied().map(|i| &mut self.args[i]) - } - - fn insert(mut self, arg: Arg) -> Self { - let name = arg.name(); - let short = arg.short(); - - let index = self.args.len(); - self.args.push(arg); - - let prev = self.long.insert(name, index); - assert!(prev.is_none(), "duplicate argument `{name}` provided"); - - if let Some(short) = short { - let prev = self.short.insert(short, index); - assert!(prev.is_none(), "duplicate argument `-{short}` provided"); - } - - self - } - - /// Parses an argument as an option. - /// - /// Returns `Ok(true)` if the argument is an option. - /// - /// Returns `Ok(false)` if the argument is not an option. - fn parse(&mut self, arg: &str, iter: &mut impl Iterator) -> Result { - // Handle short options - if let Some(mut short) = iter_short(arg) { - while let Some(c) = short.next() { - if let Some(option) = self.get_short_mut(c) { - if option.expects_value() { - let value: String = short.parse_remaining(iter).map_err(|_| { - anyhow!("a value is required for '{option}' but none was supplied") - })?; - - // Strip a leading `=` out of the value if present - option - .set_value(value.strip_prefix('=').map(Into::into).unwrap_or(value))?; - return Ok(true); - } - - option.set_present()?; - } - } - - // The argument is an option - return Ok(true); - } - - // Handle long options - if arg.starts_with("--") { - if let Some(option) = self.get_mut(arg.split_once('=').map(|(n, _)| n).unwrap_or(arg)) { - if option.expects_value() { - if let Some(v) = match_arg(option.name(), &arg, iter) { - option.set_value(v.map_err(|_| { - anyhow!("a value is required for '{option}' but none was supplied") - })?)?; - } - } else if option.name() == arg { - option.set_present()?; - } - } - - // The argument is an option - return Ok(true); - } - - // Not an option - Ok(false) - } -} - -/// Represents known cargo arguments. -/// -/// This is a subset of the arguments that cargo supports that -/// are necessary for cargo-miden to function. -#[derive(Default, Debug, Clone, Eq, PartialEq)] -pub struct CargoArguments { - /// The --color argument. - pub color: Option, - /// The (count of) --verbose argument. - pub verbose: usize, - /// The --quiet argument. - pub quiet: bool, - /// The --target argument. - pub targets: Vec, - /// The --manifest-path argument. - pub manifest_path: Option, - /// The --frozen argument. - pub frozen: bool, - /// The --locked argument. - pub locked: bool, - /// The --release argument. - pub release: bool, - /// The --offline argument. - pub offline: bool, - /// The --workspace argument. - pub workspace: bool, - /// The --package argument. - pub packages: Vec, -} - -impl CargoArguments { - /// Determines if network access is allowed based on the configuration. - pub fn network_allowed(&self) -> bool { - !self.frozen && !self.offline - } - - /// Determines if an update to the lock file is allowed based on the configuration. - pub fn lock_update_allowed(&self) -> bool { - !self.frozen && !self.locked - } - - /// Parses the arguments from the environment. - pub fn parse() -> Result { - Self::parse_from(std::env::args().skip(1)) - } - - /// Parses the arguments from an iterator. - pub fn parse_from(iter: impl Iterator) -> Result - where - T: Into, - { - let mut args = Args::default() - .single("--color", "WHEN", Some('c')) - .single("--manifest-path", "PATH", None) - .multiple("--package", "SPEC", Some('p')) - .multiple("--target", "TRIPLE", None) - .flag("--release", Some('r')) - .flag("--frozen", None) - .flag("--locked", None) - .flag("--offline", None) - .flag("--all", None) - .flag("--workspace", None) - .counting("--verbose", Some('v')) - .flag("--quiet", Some('q')); - - let mut iter = iter.map(Into::into).peekable(); - - // Skip the first argument if it is `miden` - if let Some(arg) = iter.peek() { - if arg == "miden" { - iter.next().unwrap(); - } - } - - while let Some(arg) = iter.next() { - // Break out of processing at the first `--` - if arg == "--" { - break; - } - - // Parse options - if args.parse(&arg, &mut iter)? { - continue; - } - } - - Ok(Self { - color: args - .get_mut("--color") - .unwrap() - .take_single() - .map(|v| v.parse()) - .transpose()?, - verbose: args.get("--verbose").unwrap().count(), - quiet: args.get("--quiet").unwrap().count() > 0, - manifest_path: args - .get_mut("--manifest-path") - .unwrap() - .take_single() - .map(PathBuf::from), - targets: args.get_mut("--target").unwrap().take_multiple(), - frozen: args.get("--frozen").unwrap().count() > 0, - locked: args.get("--locked").unwrap().count() > 0, - offline: args.get("--offline").unwrap().count() > 0, - release: args.get("--release").unwrap().count() > 0, - workspace: args.get("--workspace").unwrap().count() > 0 - || args.get("--all").unwrap().count() > 0, - packages: args - .get_mut("--package") - .unwrap() - .take_multiple() - .into_iter() - .map(CargoPackageSpec::new) - .collect::>()?, - }) - } -} - -/// Configuration information for cargo-miden. -/// -/// This is used to configure the behavior of cargo-miden. -#[derive(Debug)] -pub struct Config { - /// The terminal to use. - terminal: Terminal, -} - -impl Config { - /// Create a new `Config` with the given terminal. - pub fn new(terminal: Terminal) -> Result { - Ok(Self { terminal }) - } - - /// Gets a reference to the terminal for writing messages. - pub fn terminal(&self) -> &Terminal { - &self.terminal - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::iter::empty; - - #[test] - fn it_parses_flags() { - let mut args = Args::default().flag("--flag", Some('f')); - - // Test not the flag - args.parse("--not-flag", &mut empty::()).unwrap(); - let arg = args.get("--flag").unwrap(); - assert_eq!(arg.count(), 0); - - // Test the flag - args.parse("--flag", &mut empty::()).unwrap(); - assert_eq!( - args.parse("--flag", &mut empty::()) - .unwrap_err() - .to_string(), - "the argument '--flag' cannot be used multiple times" - ); - let arg = args.get_mut("--flag").unwrap(); - assert_eq!(arg.count(), 1); - arg.reset(); - - // Test not the short flag - args.parse("-rxd", &mut empty::()).unwrap(); - let arg = args.get("--flag").unwrap(); - assert_eq!(arg.count(), 0); - - // Test the short flag - args.parse("-rfx", &mut empty::()).unwrap(); - assert_eq!( - args.parse("-fxz", &mut empty::()) - .unwrap_err() - .to_string(), - "the argument '--flag' cannot be used multiple times" - ); - let arg = args.get("--flag").unwrap(); - assert_eq!(arg.count(), 1); - - // Test it prints correctly - assert_eq!(arg.to_string(), "--flag") - } - - #[test] - fn it_parses_single_values() { - let mut args = Args::default().single("--option", "VALUE", Some('o')); - - // Test not the option - args.parse("--not-option", &mut empty::()).unwrap(); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_single(), None); - - // Test missing value - assert_eq!( - args.parse("--option", &mut empty::()) - .unwrap_err() - .to_string(), - "a value is required for '--option ' but none was supplied" - ); - - // Test the option with equals - args.parse("--option=value", &mut empty::()) - .unwrap(); - assert_eq!( - args.parse("--option=value", &mut empty::()) - .unwrap_err() - .to_string(), - "the argument '--option ' cannot be used multiple times" - ); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_single(), Some("value".to_string())); - arg.reset(); - - // Test the option with space - let mut iter = ["value".to_string()].into_iter(); - args.parse("--option", &mut iter).unwrap(); - assert!(iter.next().is_none()); - let mut iter = ["value".to_string()].into_iter(); - assert_eq!( - args.parse("--option", &mut iter).unwrap_err().to_string(), - "the argument '--option ' cannot be used multiple times" - ); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_single(), Some("value".to_string())); - arg.reset(); - - // Test not the short option - args.parse("-xyz", &mut empty::()).unwrap(); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_single(), None); - - assert_eq!( - args.parse("-fo", &mut empty::()) - .unwrap_err() - .to_string(), - "a value is required for '--option ' but none was supplied" - ); - - // Test the short option without equals - args.parse("-xofoo", &mut empty::()).unwrap(); - assert_eq!( - args.parse("-zyobar", &mut iter).unwrap_err().to_string(), - "the argument '--option ' cannot be used multiple times" - ); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_single(), Some(String::from("foo"))); - - // Test the short option with equals - args.parse("-xo=foo", &mut empty::()).unwrap(); - assert_eq!( - args.parse("-zyo=bar", &mut iter).unwrap_err().to_string(), - "the argument '--option ' cannot be used multiple times" - ); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_single(), Some(String::from("foo"))); - - // Test the short option with space - let mut iter = ["value".to_string()].into_iter(); - args.parse("-xo", &mut iter).unwrap(); - let mut iter = ["value".to_string()].into_iter(); - assert_eq!( - args.parse("-zyo", &mut iter).unwrap_err().to_string(), - "the argument '--option ' cannot be used multiple times" - ); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_single(), Some(String::from("value"))); - - // Test it prints correctly - assert_eq!(arg.to_string(), "--option ") - } - - #[test] - fn it_parses_multiple_values() { - let mut args = Args::default().multiple("--option", "VALUE", Some('o')); - - // Test not the option - args.parse("--not-option", &mut empty::()).unwrap(); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_multiple(), Vec::::new()); - - // Test missing value - assert_eq!( - args.parse("--option", &mut empty::()) - .unwrap_err() - .to_string(), - "a value is required for '--option ' but none was supplied" - ); - - // Test the option with equals - args.parse("--option=foo", &mut empty::()).unwrap(); - args.parse("--option=bar", &mut empty::()).unwrap(); - args.parse("--option=baz", &mut empty::()).unwrap(); - let arg = args.get_mut("--option").unwrap(); - assert_eq!( - arg.take_multiple(), - vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),] - ); - arg.reset(); - - // Test the option with space - let mut iter = ["foo".to_string()].into_iter(); - args.parse("--option", &mut iter).unwrap(); - assert!(iter.next().is_none()); - let mut iter = ["bar".to_string()].into_iter(); - args.parse("--option", &mut iter).unwrap(); - assert!(iter.next().is_none()); - let mut iter = ["baz".to_string()].into_iter(); - args.parse("--option", &mut iter).unwrap(); - assert!(iter.next().is_none()); - let arg = args.get_mut("--option").unwrap(); - assert_eq!( - arg.take_multiple(), - vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),] - ); - arg.reset(); - - // Test not the short option - args.parse("-xyz", &mut empty::()).unwrap(); - let arg = args.get_mut("--option").unwrap(); - assert_eq!(arg.take_single(), None); - - // Test missing shot option value - assert_eq!( - args.parse("-fo", &mut empty::()) - .unwrap_err() - .to_string(), - "a value is required for '--option ' but none was supplied" - ); - - // Test the short option without equals - args.parse("-xofoo", &mut empty::()).unwrap(); - args.parse("-yobar", &mut empty::()).unwrap(); - args.parse("-zobaz", &mut empty::()).unwrap(); - let arg = args.get_mut("--option").unwrap(); - assert_eq!( - arg.take_multiple(), - vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),] - ); - - // Test the short option with equals - args.parse("-xo=foo", &mut empty::()).unwrap(); - args.parse("-yo=bar", &mut empty::()).unwrap(); - args.parse("-zo=baz", &mut empty::()).unwrap(); - let arg = args.get_mut("--option").unwrap(); - assert_eq!( - arg.take_multiple(), - vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),] - ); - - // Test the short option with space - let mut iter = ["foo".to_string()].into_iter(); - args.parse("-xo", &mut iter).unwrap(); - let mut iter = ["bar".to_string()].into_iter(); - args.parse("-yo", &mut iter).unwrap(); - let mut iter = ["baz".to_string()].into_iter(); - args.parse("-zo", &mut iter).unwrap(); - let arg = args.get_mut("--option").unwrap(); - assert_eq!( - arg.take_multiple(), - vec!["foo".to_string(), "bar".to_string(), "baz".to_string(),] - ); - - // Test it prints correctly - assert_eq!(arg.to_string(), "--option ") - } - - #[test] - fn it_parses_counting_flag() { - let mut args = Args::default().counting("--flag", Some('f')); - - // Test not the the flag - args.parse("--not-flag", &mut empty::()).unwrap(); - let arg = args.get("--flag").unwrap(); - assert_eq!(arg.count(), 0); - - // Test the flag - args.parse("--flag", &mut empty::()).unwrap(); - args.parse("--flag", &mut empty::()).unwrap(); - args.parse("--flag", &mut empty::()).unwrap(); - let arg = args.get_mut("--flag").unwrap(); - assert_eq!(arg.count(), 3); - arg.reset(); - - // Test the short flag - args.parse("-xfzf", &mut empty::()).unwrap(); - args.parse("-pfft", &mut empty::()).unwrap(); - args.parse("-abcd", &mut empty::()).unwrap(); - let arg = args.get_mut("--flag").unwrap(); - assert_eq!(arg.count(), 4); - - // Test it prints correctly - assert_eq!(arg.to_string(), "--flag") - } - - #[test] - fn it_parses_cargo_arguments() { - let args: CargoArguments = - CargoArguments::parse_from(["miden", "build", "--workspace"].into_iter()).unwrap(); - assert_eq!( - args, - CargoArguments { - color: None, - verbose: 0, - quiet: false, - targets: Vec::new(), - manifest_path: None, - release: false, - frozen: false, - locked: false, - offline: false, - workspace: true, - packages: Vec::new(), - } - ); - - let args = CargoArguments::parse_from( - [ - "miden", - "publish", - "-vvv", - "--color=auto", - "--manifest-path", - "Cargo.toml", - "--release", - "--package", - "package1", - "-p=package2@1.1.1", - "--target=foo", - "--target", - "bar", - "--quiet", - "--frozen", - "--locked", - "--offline", - "--all", - "--not-an-option", - ] - .into_iter(), - ) - .unwrap(); - assert_eq!( - args, - CargoArguments { - color: Some(Color::Auto), - verbose: 3, - quiet: true, - targets: vec!["foo".to_string(), "bar".to_string()], - manifest_path: Some("Cargo.toml".into()), - release: true, - frozen: true, - locked: true, - offline: true, - workspace: true, - packages: vec![ - CargoPackageSpec { - name: "package1".to_string(), - version: None - }, - CargoPackageSpec { - name: "package2".to_string(), - version: Some(Version::parse("1.1.1").unwrap()) - } - ], - } - ); - } -} diff --git a/tools/cargo-miden/src/lib.rs b/tools/cargo-miden/src/lib.rs deleted file mode 100644 index 856c36f85..000000000 --- a/tools/cargo-miden/src/lib.rs +++ /dev/null @@ -1,127 +0,0 @@ -use std::path::PathBuf; - -use crate::run_cargo_command::run_cargo_command; -use anyhow::bail; -use cargo_component::load_metadata; -use cargo_component_core::terminal::Terminal; -use clap::{CommandFactory, Parser}; -use config::CargoArguments; -use new_project::NewCommand; - -mod build; -pub mod config; -mod new_project; -mod run_cargo_command; -mod target; - -fn version() -> &'static str { - option_env!("CARGO_VERSION_INFO").unwrap_or(env!("CARGO_PKG_VERSION")) -} - -/// The list of commands that are built-in to `cargo-miden`. -const BUILTIN_COMMANDS: &[&str] = &[ - "miden", // for indirection via `cargo miden` - "new", -]; - -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). - \n\ - See `cargo help` for more information on available cargo commands."; - -/// Cargo integration for Miden -#[derive(Parser)] -#[clap( - bin_name = "cargo", - version, - propagate_version = true, - arg_required_else_help = true, - after_help = AFTER_HELP -)] -#[command(version = version())] -enum CargoMiden { - /// Cargo integration for Miden - #[clap(subcommand, hide = true, after_help = AFTER_HELP)] - Miden(Command), // indirection via `cargo miden` - #[clap(flatten)] - Command(Command), -} - -#[derive(Parser)] -enum Command { - New(NewCommand), -} - -fn detect_subcommand(args: I) -> Option -where - I: IntoIterator, - T: Into + Clone, -{ - let mut iter = args.into_iter().map(Into::into).skip(1).peekable(); - - // Skip the first argument if it is `miden` (i.e. `cargo miden`) - if let Some(arg) = iter.peek() { - if arg == "miden" { - iter.next().unwrap(); - } - } - - for arg in iter { - // Break out of processing at the first `--` - if arg == "--" { - break; - } - - if !arg.starts_with('-') { - return Some(arg); - } - } - - None -} - -pub fn run(args: T, terminal: &Terminal) -> anyhow::Result> -where - T: Iterator, -{ - let args = args.collect::>(); - let subcommand = detect_subcommand(args.clone().into_iter()); - - let outputs = match subcommand.as_deref() { - // Check for built-in command or no command (shows help) - 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()?], - }, - } - } - - // If no subcommand was detected, - None => { - // Attempt to parse the supported CLI (expected to fail) - CargoMiden::parse_from(args); - - // If somehow the CLI parsed correctly despite no subcommand, - // print the help instead - CargoMiden::command().print_long_help()?; - 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)?; - if metadata.packages.is_empty() { - bail!( - "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(); - run_cargo_command(&metadata, subcommand.as_deref(), &cargo_args, &spawn_args)? - } - }; - Ok(outputs) -} diff --git a/tools/cargo-miden/src/main.rs b/tools/cargo-miden/src/main.rs deleted file mode 100644 index df62bf60a..000000000 --- a/tools/cargo-miden/src/main.rs +++ /dev/null @@ -1,30 +0,0 @@ -use cargo_component_core::terminal::{Terminal, Verbosity}; -use cargo_miden::{config::CargoArguments, run}; - -fn main() -> anyhow::Result<()> { - // Initialize logger - let mut builder = env_logger::Builder::from_env("CARGO_MIDEN_TRACE"); - builder.format_indent(Some(2)); - builder.format_timestamp(None); - builder.init(); - - let cargo_args = CargoArguments::parse_from(std::env::args())?; - let terminal = Terminal::new( - if cargo_args.quiet { - Verbosity::Quiet - } else { - match cargo_args.verbose { - 0 => Verbosity::Normal, - _ => Verbosity::Verbose, - } - }, - cargo_args.color.unwrap_or_default(), - ); - - if let Err(e) = run(std::env::args(), &terminal) { - terminal.error(format!("{e:?}"))?; - std::process::exit(1); - } - - Ok(()) -} diff --git a/tools/cargo-miden/src/new_project.rs b/tools/cargo-miden/src/new_project.rs deleted file mode 100644 index 7419364a3..000000000 --- a/tools/cargo-miden/src/new_project.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::path::PathBuf; - -use anyhow::Context; -use cargo_generate::GenerateArgs; -use cargo_generate::TemplatePath; -use clap::Args; - -/// Create a new Miden project at -#[derive(Args)] -#[clap(disable_version_flag = true)] -pub struct NewCommand { - /// The path for the generated package. - #[clap(value_name = "path")] - pub path: PathBuf, -} - -impl NewCommand { - pub fn exec(self) -> anyhow::Result { - let name = self.path - .file_name() - .ok_or_else(|| { - anyhow::anyhow!("Failed to get the last segment of the provided path for the project name") - })? - .to_str() - .ok_or_else(|| { - anyhow::anyhow!("The last segment of the provided path must be valid UTF8 to generate a valid project name") - })? - .to_string(); - - let generate_args = GenerateArgs { - template_path: TemplatePath { - git: Some("https://github.com/0xPolygonMiden/rust-templates".into()), - auto_path: Some("library".into()), - ..Default::default() - }, - destination: self - .path - .parent() - .map(|p| { - use path_absolutize::Absolutize; - p.absolutize().map(|p| p.to_path_buf()) - }) - .transpose() - .context("Failed to convert destination path to an absolute path")?, - name: Some(name), - force_git_init: true, - verbose: true, - ..Default::default() - }; - cargo_generate::generate(generate_args) - .context("Failed to scaffold new Miden project from the template")?; - return Ok(self.path); - } -} diff --git a/tools/cargo-miden/src/run_cargo_command.rs b/tools/cargo-miden/src/run_cargo_command.rs deleted file mode 100644 index f62276a60..000000000 --- a/tools/cargo-miden/src/run_cargo_command.rs +++ /dev/null @@ -1,127 +0,0 @@ -use anyhow::bail; -use cargo_metadata::Metadata; -use std::path::PathBuf; -use std::process::Command; - -use crate::build::build_masm; -use crate::config::CargoArguments; -use crate::target::{install_wasm32_wasi, WASM32_WASI_TARGET}; - -fn is_wasm_target(target: &str) -> bool { - target == WASM32_WASI_TARGET -} - -/// Runs the cargo command as specified in the configuration. -/// -/// Returns any relevant output artifacts. -pub fn run_cargo_command( - metadata: &Metadata, - subcommand: Option<&str>, - cargo_args: &CargoArguments, - spawn_args: &[String], -) -> anyhow::Result> { - let cargo = std::env::var("CARGO") - .map(PathBuf::from) - .ok() - .unwrap_or_else(|| PathBuf::from("cargo")); - - let mut args = spawn_args.iter().peekable(); - if let Some(arg) = args.peek() { - if *arg == "miden" { - args.next().unwrap(); - } - } - - // Spawn the actual cargo command - log::debug!( - "spawning cargo `{cargo}` with arguments `{args:?}`", - cargo = cargo.display(), - args = args.clone().collect::>(), - ); - - let mut cmd = Command::new(&cargo); - cmd.args(args); - - let is_build = matches!(subcommand, Some("b") | Some("build")); - - // Handle the target for build commands - if is_build { - install_wasm32_wasi()?; - - // 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)) { - cmd.arg("--target").arg(WASM32_WASI_TARGET); - } - } - - match cmd.status() { - Ok(status) => { - if !status.success() { - bail!("cargo failed with exit code {}", status.code().unwrap_or(1)); - } - } - Err(e) => { - bail!("failed to spawn `{cargo}`: {e}", cargo = cargo.display()); - } - } - let mut outputs = Vec::new(); - if is_build { - log::debug!("searching for WebAssembly modules to compile to MASM"); - let targets = cargo_args - .targets - .iter() - .map(String::as_str) - .filter(|t| is_wasm_target(t)) - .chain(cargo_args.targets.is_empty().then_some(WASM32_WASI_TARGET)); - - for target in targets { - let out_dir = metadata - .target_directory - .join(target) - .join(if cargo_args.release { - "release" - } else { - "debug" - }); - - let miden_out_dir = - metadata - .target_directory - .join("miden") - .join(if cargo_args.release { - "release" - } else { - "debug" - }); - if !miden_out_dir.exists() { - std::fs::create_dir_all(&miden_out_dir)?; - } - - for package in &metadata.packages { - let is_bin = package.targets.iter().any(|t| t.is_bin()); - - // First try for .wasm - let path = out_dir.join(&package.name).with_extension("wasm"); - if path.exists() { - let output = - build_masm(path.as_std_path(), miden_out_dir.as_std_path(), is_bin)?; - outputs.push(output); - } else { - let path = out_dir - .join(package.name.replace('-', "_")) - .with_extension("wasm"); - if path.exists() { - let output = - build_masm(path.as_std_path(), miden_out_dir.as_std_path(), is_bin)?; - outputs.push(output); - } else { - log::debug!("no output found for package `{name}`", name = package.name); - bail!("Cargo build failed, no Wasm artifact found"); - } - } - } - } - } - - Ok(outputs) -} diff --git a/tools/cargo-miden/src/target.rs b/tools/cargo-miden/src/target.rs deleted file mode 100644 index 653979969..000000000 --- a/tools/cargo-miden/src/target.rs +++ /dev/null @@ -1,58 +0,0 @@ -use anyhow::{bail, Result}; -use std::{ - env, - path::PathBuf, - process::{Command, Stdio}, -}; - -pub const WASM32_WASI_TARGET: &str = "wasm32-wasi"; - -pub fn install_wasm32_wasi() -> Result<()> { - log::info!("Installing {WASM32_WASI_TARGET} target"); - let sysroot = get_sysroot()?; - if sysroot.join("lib/rustlib/wasm32-wasi").exists() { - return Ok(()); - } - - if env::var_os("RUSTUP_TOOLCHAIN").is_none() { - bail!( - "failed to find the `wasm32-wasi` target \ - and `rustup` is not available. If you're using rustup \ - make sure that it's correctly installed; if not, make sure to \ - install the `wasm32-wasi` target before using this command" - ); - } - - let output = Command::new("rustup") - .arg("target") - .arg("add") - .arg(WASM32_WASI_TARGET) - .stderr(Stdio::inherit()) - .stdout(Stdio::inherit()) - .output()?; - - if !output.status.success() { - bail!("failed to install the `wasm32-wasi` target"); - } - - Ok(()) -} - -fn get_sysroot() -> Result { - let output = Command::new("rustc") - .arg("--print") - .arg("sysroot") - .output()?; - - if !output.status.success() { - bail!( - "failed to execute `rustc --print sysroot`, \ - command exited with error: {output}", - output = String::from_utf8_lossy(&output.stderr) - ); - } - - let sysroot = PathBuf::from(String::from_utf8(output.stdout)?.trim()); - - Ok(sysroot) -} diff --git a/tools/cargo-miden/tests/build.rs b/tools/cargo-miden/tests/build.rs deleted file mode 100644 index b68be32d8..000000000 --- a/tools/cargo-miden/tests/build.rs +++ /dev/null @@ -1,42 +0,0 @@ -use cargo_component_core::terminal; -use cargo_miden::run; -use std::env; -use std::fs; - -// NOTE: This test sets the current working directory so don't run it in parallel with tests -// that depend on the current directory - -#[test] -fn build_new_project_from_template() { - let restore_dir = env::current_dir().unwrap(); - let temp_dir = env::temp_dir(); - env::set_current_dir(&temp_dir).unwrap(); - let project_name = "test-proj"; - let expected_new_project_dir = &temp_dir.join(project_name); - if expected_new_project_dir.exists() { - fs::remove_dir_all(expected_new_project_dir).unwrap(); - } - let args = ["cargo", "miden", "new", project_name] - .into_iter() - .map(|s| s.to_string()); - let terminal = terminal::Terminal::new(terminal::Verbosity::Verbose, terminal::Color::Auto); - let outputs = run(args, &terminal).expect("Failed to create new project"); - let new_project_path = outputs.first().unwrap().canonicalize().unwrap(); - dbg!(&new_project_path); - assert!(new_project_path.exists()); - assert_eq!( - new_project_path, - expected_new_project_dir.canonicalize().unwrap() - ); - env::set_current_dir(&new_project_path).unwrap(); - let args = ["cargo", "miden", "build", "--release"] - .iter() - .map(|s| s.to_string()); - let outputs = run(args, &terminal).expect("Failed to compile"); - let expected_masm_path = outputs.first().unwrap(); - dbg!(&expected_masm_path); - assert!(expected_masm_path.exists()); - assert!(expected_masm_path.metadata().unwrap().len() > 0); - env::set_current_dir(restore_dir).unwrap(); - fs::remove_dir_all(new_project_path).unwrap(); -} diff --git a/tools/cargo-miden/tests/mod.rs b/tools/cargo-miden/tests/mod.rs deleted file mode 100755 index 3a146708a..000000000 --- a/tools/cargo-miden/tests/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod build; -mod utils; diff --git a/tools/cargo-miden/tests/utils.rs b/tools/cargo-miden/tests/utils.rs deleted file mode 100644 index de17a8abd..000000000 --- a/tools/cargo-miden/tests/utils.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::env; -use std::path::PathBuf; - -#[allow(dead_code)] -pub(crate) fn get_test_path(test_dir_name: &str) -> PathBuf { - let mut test_dir = - PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set")); - test_dir.push("tests"); - test_dir.push("data"); - test_dir.push(test_dir_name); - test_dir -} diff --git a/tools/filecheck/Cargo.toml b/tools/filecheck/Cargo.toml deleted file mode 100644 index b7952868e..000000000 --- a/tools/filecheck/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "filecheck" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -anyhow.workspace = true -clap.workspace = true -lit = { git = "https://github.com/bitwalker/lit", branch = "main" } diff --git a/tools/filecheck/src/main.rs b/tools/filecheck/src/main.rs deleted file mode 100644 index 71d0bfecf..000000000 --- a/tools/filecheck/src/main.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::env; -use std::path::PathBuf; - -use anyhow::{anyhow, bail}; -use clap::Parser; -use lit::event_handler::Default as EventHandler; - -#[derive(Parser)] -#[clap(version, about, long_about = None)] -#[command(arg_required_else_help = true)] -struct Config { - /// Path to directory containing the lit tests to be run - #[arg(value_name = "DIR")] - tests: PathBuf, - /// Specify one or more file extensions to include when searching for test inputs - #[arg(long = "type", short = 't', default_value = "hir")] - file_types: Vec, - /// Define one or more variables to make available in test commands - /// - /// You must specify each one in `key=value` format, comma-separated. - #[arg(long = "define", short = 'D')] - defines: Vec, -} - -pub fn main() -> anyhow::Result<()> { - let config = Config::parse(); - - let cwd = env::current_dir().unwrap(); - let test_path = config.tests.as_path(); - let lit_dir = if test_path.is_file() { - test_path.parent().unwrap().to_str().unwrap() - } else { - test_path.to_str().unwrap() - }; - - let midenc_exe = cwd.join("bin/midenc"); - if !midenc_exe.is_file() { - bail!( - "expected to find midenc at {}, but it either doesn't exist, or is not a file", - midenc_exe.display() - ); - } - - let mut defines = Vec::with_capacity(config.defines.len()); - for define in config.defines.iter() { - if let Some((key, value)) = define.split_once('=') { - defines.push((key.to_string(), value.to_string())); - } else { - bail!( - "invalid variable definition, expected 'key=value', got '{}'", - define - ); - } - } - - lit::run::tests(EventHandler::default(), move |runner| { - runner.add_search_path(test_path.to_str().unwrap()); - for ty in config.file_types.iter() { - runner.add_extension(ty.as_str()); - } - - runner - .constants - .insert("tests".to_string(), lit_dir.to_string()); - runner.constants.insert( - "midenc".to_string(), - midenc_exe.to_str().unwrap().to_string(), - ); - - for (k, v) in defines.drain(..) { - runner.constants.insert(k, v); - } - }) - .map_err(|_| anyhow!("lit tests failed, see output for details")) -}